
Klant: Useq
Project: Spatial Organ Atlas
Dataset: Kidney
date: 12 56 Tue 25 April, 2023

loading dependencies Please make sure the following
packages are installed and required libraries can be loaded:
- install.packages(“pkgbuild”) // pkgbuild::check_build_tools()
- install.packages(“devtools”)
- devtools::install_github(“Nanostring-Biostats/NanoStringNCTools”)
- devtools::install_github(“Nanostring-Biostats/GeomxTools”, ref =
“dev”)
- BiocManager::install(“GeoMxWorkflows”)
- devtools::install_github(“DavisLaboratory/standR”)
- BiocManager::install(“SpatialDecon”)
- BiocManager::install(“GSVA”)
- install.packages(“plotly”)
- install.packages(“DT”)
- install.packages(“msigdbr”)
- install.packages(“digest”)
- install.packages(“rmarkdown”)
- install.packages(“kable”)
- BiocManager::install(“EBImage”)
- install.packages(‘scattermore’)
- install.packages(‘pbapply’)
- install.packages(‘plotrix’)
- install.packages(‘ggtext’)
- install.packages(‘RBioFormats’)
- BiocManager::install(“aoles/RBioFormats”)
- install.packages(“C:/Users/inijman/Downloads/SpatialOmicsOverlay-0.99.13-beta.tar.gz”,
dependencies = TRUE, repos = NULL)
- devtools::install_github(“DavisLaboratory/standR”)
- BiocManager::install(“clusterProfiler”)
- BiocManager::install(“pathview”)
#load libraries
#stack_size <- getOption("pandoc.stack.size", default = "512m")
#options(java.parameters = c("-XX:+UseConcMarkSweepGC", "-Xmx8192m, -XX:MetaspaceSize=1024M"))
options(java.parameters = c("-XX:+UseConcMarkSweepGC", "-Xmx8192m"))
library(NanoStringNCTools)
library(GeomxTools)
library(GeoMxWorkflows)
library(SpatialDecon)
library(GSVA) #for pathway analyses
library(msigdbr) #for molecular signatures in pathway analyses
library(knitr)
library(dplyr)
library(ggforce)
library(ggplot2)
library(scales) # for percent
library(reshape2) # for melt
library(cowplot) # for plot_grid
library(umap)
library(Rtsne)
library(pheatmap) # for pheatmap
library(ggrepel)
library(scales) #for ggplot peaudolog to prevent errors on log(0)
library(DT)
library(plotly)
library(gridExtra)
library(RColorBrewer)
library(SpatialOmicsOverlay)
library(gt)
library(tidyverse)
library(clusterProfiler)
library(fgsea)
library(pathview)
library(png)
library(readxl)
library(viridis)
library(standR)
library(SpatialExperiment)
library(limma)
library(ggalluvial)
library(scater)
BiocManager::install("sva")
library(sva)
#BiocManager::install("preprocessCore") #quantile norm
library(preprocessCore)
#install.packages("Polychrome") #Get colors
#library(Polychrome)
library(infercnv) # CNV
library("ape")
library("Biostrings")
library("ggtree")
library(tidytree)
library(ggpubr)
1 loading base files
# Reference the main folder 'file.path' containing the sub-folders with each data file type:
datadir<-file.path("L:/pkloosterman/Github/DKD_Kidney/")
To locate a specific file path replace the above line with datadir
<- file.path(“~/Folder/SubFolder/DataLocation”) replace the Folder,
SubFolder, DataLocation as needed. The DataLocation folder should
contain a dccs, pkcs, and annotation folder with each set of files
present as needed automatically list files in each directory for
use.
Take care you import a column with nuclei count separately if
you want.
DCCFiles <- dir(file.path(datadir, "dccs"), pattern = ".dcc$",
full.names = TRUE, recursive = TRUE)
PKCFiles <- dir(file.path(datadir, "pkcs"), pattern = ".pkc$",
full.names = TRUE, recursive = TRUE)
SampleAnnotationFile <-
dir(file.path(datadir, "annotation"), pattern = "^[^~]",
full.names = TRUE, recursive = TRUE)
2 load data
Data <-
readNanoStringGeoMxSet(dccFiles = DCCFiles,
pkcFiles = PKCFiles,
phenoDataFile = SampleAnnotationFile,
phenoDataSheet = "Template",
phenoDataDccColName = "Sample_ID",
protocolDataColNames = c("aoi", "roi"),
experimentDataColNames = c("panel"))
#save data to prevent loading time for retakes
#saveData<-Data
#Data<-saveData
#change Data column names and manual correction of spelling errors
Data@phenoData@data[["slide_name"]]<-Data@phenoData@data[["slide name"]]
Data@phenoData@data[["slide name"]]<- NULL
#+1 references the slide name column
ann_size<-length(colnames(Data@phenoData@data)[grepl("ANN",colnames(Data@phenoData@data))])+1
ann_names<-c(colnames(Data@phenoData@data)[grepl("ANN",colnames(Data@phenoData@data))],"slide_name")
# Feel free to change the order of which colors are appointed.
color<-c("#A349A4", "#FFFF33", "#E7298A", "#091833", "#1B9E77", "#D95F02", "#7570B3", "#66A61E", "#E6AB02", "#8DD3C7", "#9F000F", "#BEBADA", "#FB8072", "#80B1D3", "#FDB462", "#B3DE69", "#FCCDE5", "#D9D9D9", "#BC80BD", "#CCEBC5", "#FFED6F", "#377EB8", "#984EA3", "#4DAF4A", "#FF71CE", "#FF7F00", "#A6CEE3", "#1F78B4", "#B2DF8A", "#33A02C", "#FB9A99", "#E31A1C", "#FDBF6F", "#CAB2D6", "#6A3D9A", "#FFFF99", "#B15928")
# Use count and count_max to set the range of color needed for each column.
# With setNames() the color code is linked to each unique individual value of each column.
count=1
color_list = list()
for (ann in ann_names) {
count_max = count+length(unique(Data@phenoData@data[[ann]]))-1
color_list[[ann]]<-setNames(color[count:count_max], unique(Data@phenoData@data[[ann]]))
count=count_max+1
}
value_names = c()
for (ann in ann_names) {
value_names <- c(value_names, unique(Data@phenoData@data[[ann]]))
}
color_df <- as.data.frame(value_names)
color_df$color <- color[0:length(value_names)]
color_df %>%
mutate(color = fct_inorder(color)) |>
gt() %>%
data_color(columns = color, colors = as.character(color)) %>%
tab_options(container.height = 500)
| value_names |
color |
| DKD |
#A349A4 |
| normal |
#FFFF33 |
| disease3 |
#E7298A |
| disease4 |
#091833 |
| normal3 |
#1B9E77 |
| normal4 |
#D95F02 |
| disease1B |
#7570B3 |
| disease2B |
#66A61E |
| normal2B |
#E6AB02 |
| glomerulus |
#8DD3C7 |
| tubule |
#9F000F |
| abnormal |
#BEBADA |
| NA |
#FB8072 |
| normal |
#80B1D3 |
| niormal |
#FDB462 |
| disease3 |
#B3DE69 |
| disease4 |
#FCCDE5 |
| normal3 |
#D9D9D9 |
| normal4 |
#BC80BD |
| disease1B |
#CCEBC5 |
| disease2B |
#FFED6F |
| normal2B |
#377EB8 |
paste("Reads from following runs used: ",unique(pData(protocolData(Data))$SeqSetId))
## [1] "Reads from following runs used: "
3 Study design
pkcs <- annotation(Data)
modules <- gsub(".pkc", "", pkcs)
kable(data.frame(PKCs = pkcs, modules = modules))
| TAP_H_WTA_v1.0.pkc |
TAP_H_WTA_v1.0 |
Select the annotations we want to show, use `` to surround column
names with spaces or special symbols
#count_mat <- dplyr::count(pData(Data), ANN1,ANN2,slide_name)
count_mat <- pData(Data) %>% dplyr::count(pData(Data)[c(ann_names)])
Simplify the slide names if required
# count_mat$slide_name <- gsub("disease", "d", gsub("normal", "n", count_mat$slide_name))
#count_mat$path_ann <- gsub("i", "", count_mat$path_ann) #correcting spelling error
Gather the data and plot in order: class, slide name, region,
segment
test_gr <- gather_set_data(count_mat, 1:all_of(ann_size))
test_gr$x <- factor(test_gr$x, labels = ann_names)
Plot Sankey
ggplot(test_gr, height = 10, width = 10, aes(x, id = id, split = y, value = n)) +
geom_parallel_sets(aes(fill = ANN2), alpha = 0.5, axis.width = 0.1) +
geom_parallel_sets_axes(axis.width = 0.2) +
geom_parallel_sets_labels(color = "white", size = 5) +
theme_classic(base_size = 12) +
theme(legend.position = "bottom",
axis.ticks.y = element_blank(),
axis.line = element_blank(),
axis.text.y = element_blank()) +
scale_y_continuous(expand = expansion(0)) +
scale_x_discrete(expand = expansion(0)) +
labs(x = "", y = "") +
scale_fill_manual(values=color_list$ANN2) +
annotate(geom = "segment", x = 4.25, xend = 4.25,
y = 10, yend = 61, lwd = 2) +
annotate(geom = "text", x = 4.19, y = 25, angle = 90, size = 5,
hjust = 0.5, label = "50 segments")

4 QC & Pre-processing
Shift counts to one
#shift any expression counts with a value of 0 to 1 to enable in downstream transformations.
Data <- shiftCountsOne(Data, useDALogic = TRUE)
4.1 Segment QC
We first assess sequencing quality and adequate tissue sampling for
every ROI/AOI segment.
Every ROI/AOI segment will be tested for:
Raw sequencing reads: segments with >1000 raw reads are removed. %
Aligned,% Trimmed, or % Stitched sequencing reads: segments below ~80%
for one or more of these QC parameters are removed. % Sequencing
saturation ([1-deduplicated reads/aligned reads]%): segments below ~50%
require additional sequencing to capture full sample diversity and are
not typically analyzed until improved. Negative Count: this is the
geometric mean of the several unique negative probes in the GeoMx panel
that do not target mRNA and establish the background count level per
segment; segments with low negative counts (1-10) are not necessarily
removed but may be studied closer for low endogenous gene signal and/or
insufficient tissue sampling. No Template Control (NTC) count: values
>1,000 could indicate contamination for the segments associated with
this NTC; however, in cases where the NTC count is between 1,000-
10,000, the segments may be used if the NTC data is uniformly low (e.g.
0-2 counts for all probes). Nuclei: >100 nuclei per segment is
generally recommended; however, this cutoff is highly study/tissue
dependent and may need to be reduced; what is most important is
consistency in the nuclei distribution for segments within the study.
Area: generally correlates with nuclei; a strict cutoff is not generally
applied based on area.
4.1.1 Select Segment QC
First, we select the QC parameter cutoffs, against which our ROI/AOI
segments will be tested and flagged appropriately. We have selected the
appropriate study-specific parameters for this study. Note: the default
QC values recommended above are advised when surveying a new dataset for
the first time.
Default QC cutoffs are commented in () adjacent to the respective
parameters study-specific values were selected after visualizing the QC
results in more detail below
QC_params <-
list(minSegmentReads = 1000, # Minimum number of reads (1000)
percentTrimmed = 80, # Minimum % of reads trimmed (80%)
percentStitched = 80, # Minimum % of reads stitched (80%)
percentAligned = 75, # Minimum % of reads aligned (80%)
percentSaturation = 50, # Minimum sequencing saturation (50%)
minNegativeCount = 1, # Minimum negative control counts (10)
maxNTCCount = 9000, # Maximum counts observed in NTC well (1000)
minNuclei = 20, # Minimum # of nuclei estimated (100)
minArea = 1000) # Minimum segment area (5000)
Data <-
setSegmentQCFlags(Data, qcCutoffs = QC_params)
cat("pre-QC features:", dim(Data)[1], "\npre-QC samples:", dim(Data)[2])
## pre-QC features: 18642
## pre-QC samples: 276
#Table for clarification
QCparams_df <- data.frame (
items = c("minSegmentReads","percentTrimmed","percentStitched","percentAligned","percentSaturation",
"minNegativeCount","maxNTCCount","minNuclei","minArea"),
defaults = c(1000,80,80,80,50,10,1000,100,5000),
actual = c(QC_params$minSegmentReads,QC_params$percentTrimmed,QC_params$percentStitched,QC_params$percentAligned,QC_params$percentSaturation,QC_params$minNegativeCount,QC_params$maxNTCCount,QC_params$minNuclei,QC_params$minArea)
)
datatable(QCparams_df, rownames=FALSE,
caption = "QC thresholds",
extensions = 'Buttons', options = list (
dom = 'Bftrip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
)
)
Collate QC Results
QCResults <- protocolData(Data)[["QCFlags"]]
flag_columns <- colnames(QCResults)
QC_Summary <- data.frame(Pass = colSums(!QCResults[, flag_columns]),
Warning = colSums(QCResults[, flag_columns]))
QCResults$QCStatus <- apply(QCResults, 1L, function(x) {
ifelse(sum(x) == 0L, "PASS", "WARNING")
})
QC_Summary["TOTAL FLAGS", ] <-
c(sum(QCResults[, "QCStatus"] == "PASS"),
sum(QCResults[, "QCStatus"] == "WARNING"))
col_by <- "ANN1"
col_by_plate <- "Plate_ID"
4.2 Graphical summaries of QC
statistics
Use the tab-menu to navigate!
QC_histogram <- function(assay_data = NULL,
annotation = NULL,
fill_by = NULL,
thr = NULL,
scale_trans = NULL) {
plt <- ggplot(assay_data,
aes_string(x = paste0("unlist(`", annotation, "`)"),
fill = fill_by)) +
geom_histogram(bins = 50) +
geom_vline(xintercept = thr, lty = "dashed", color = "black") +
theme_bw() + guides(fill = "none") +
facet_wrap(as.formula(paste("~", fill_by)), nrow = 4) +
scale_fill_manual(values=color_list$ANN1) +
labs(x = annotation, y = "segments, #", title = annotation)
if(!is.null(scale_trans)) {
plt <- plt +
scale_x_continuous(trans = scale_trans)
}
plt
}
Trimmed
QC_histogram(sData(Data), "Trimmed (%)", col_by, QC_params$percentTrimmed)

Stiched (%)
QC_histogram(sData(Data), "Stitched (%)", col_by, QC_params$percentStitched)

Aligned (%)
QC_histogram(sData(Data), "Aligned (%)", col_by,QC_params$percentAligned)

Sequencing Saturation (%)
QC_histogram(sData(Data), "Saturated (%)", col_by, QC_params$percentSaturation) +
labs(title = "Sequencing Saturation (%)",
x = "Sequencing Saturation (%)")

Area
QC_histogram(sData(Data), "area", col_by, QC_params$minArea, scale_trans = "log10")

Nuclei count
QC_histogram(sData(Data), "nuclei", col_by, QC_params$minNuclei)

DuplicationRate
ggplot(pData(protocolData(Data)),
aes(x = Plate_ID, fill=Plate_ID,
y = (DeduplicatedReads/Raw))) +
geom_violin() +
geom_jitter(width = .2) +
labs(y = "Deduplicated / Raw reads") +
scale_y_continuous(labels = scales::percent) +
theme_bw()

Negprobes vs Endogenous
tmp_target_Data <- aggregateCounts(Data)
#get negative probe data
negs<-subset(tmp_target_Data,CodeClass=="Negative")
p1<-ggplot(pData(negs),
aes(x = ANN2, fill = ANN2,
y = assayDataElement(negs, elt = "exprs"))) +
geom_violin() +
geom_jitter(width = .2) +
labs(y = "Negative probes Expression") +
scale_y_continuous(limits = c(1,3000), trans = "log2") +
theme_bw() + coord_flip()
# get endogenous probe data
end<-subset(tmp_target_Data,CodeClass=="Endogenous")
p2<-ggplot(pData(end),
aes(x = ANN2, fill = ANN2,
y = colMeans(assayDataElement(end, elt = "exprs")))) +
geom_violin() +
geom_jitter(width = .2) +
labs(y = "Endogenous probes Expression (mean)") +
scale_y_continuous(limits = c(1,3000),trans = "log2") +
theme_bw() + coord_flip()
pl <-list(p1,p2)
plot_grid(plotlist=pl, nrow=2, align='v')

Neg_probe reads compared to raw_reads
# make background total neg probe count
fdata_df<-fData(Data)
negprobesnames<-rownames(fdata_df[fdata_df$Negative==TRUE,])
temp_exp<-assayDataElement(Data,elt='exprs')
negprobe_expr_fd<-temp_exp[rownames(temp_exp) %in% negprobesnames,]
tot_neg_ctrl_reads<-colSums(negprobe_expr_fd)
tot_dedup_reads<-pData(protocolData(Data))$DeduplicatedReads
df<-data.frame('aoi'= names(tot_neg_ctrl_reads),'tot_dedup_reads' = as.numeric(tot_dedup_reads),'tot_neg_ctrl_reads'=as.numeric(tot_neg_ctrl_reads))
df<-melt(df,id="aoi")
ggplot(df,aes(fill=variable,y=value,x=aoi)) +
geom_bar(position="identity",stat="identity") +
scale_y_continuous(trans = log2_trans()) +
theme(legend.position="bottom",axis.text.x = element_blank(),axis.ticks.x=element_blank())

Duplicated reads vs Background
# get dcc per plate. sum negprobe counts/dcc/plate
ggplot(pData(protocolData(Data)),
aes(x = Plate_ID, fill=Plate_ID,
y = DeduplicatedReads)) +
geom_violin() +
geom_jitter(width = .2) +
labs(y = "Deduplicated / Raw reads") +
scale_y_log10()+
geom_hline(data =pData(protocolData(Data)) ,
aes(yintercept = NTC, colour=Plate_ID)) +
theme_bw()

Duplicated reads vs ROIarea
temp_df<-cbind(pData(Data),pData(protocolData(Data)),dcc=rownames(pData(Data)))
ggplot(temp_df,
aes(x = dcc, colour=slide_name,
y = (DeduplicatedReads/area) )) +
geom_point() +
ylim(0,20) +
labs(y = "Deduplicated reads / ROI area") +
theme(axis.text.x = element_text(size =6, angle=90, hjust=1) )

Duplicated reads vs nuclei
temp_df<-cbind(pData(Data),pData(protocolData(Data)),dcc=rownames(pData(Data)))
ggplot(temp_df,
aes(x = dcc, colour=slide_name,
y = (DeduplicatedReads/nuclei) )) +
geom_point() +
ylim(0,50) + # Adjust per project
labs(y = "Deduplicated reads / nuclei") +
theme(axis.text.x = element_text(size =6, angle=90, hjust=1) )

4.3 Process Negative GeoMeans
# Calculate the negative geometric means for each module
# It will show only the negative probes geomean, so expect less segments.
negativeGeoMeans <-
esBy(negativeControlSubset(Data),
GROUP = "Module",
FUN = function(x) {
assayDataApply(x, MARGIN = 2, FUN = ngeoMean, elt = "exprs")
})
protocolData(Data)[["NegGeoMean"]] <- negativeGeoMeans
negCols <- paste0("NegGeoMean_", modules)
pData(Data)[, negCols] <- sData(Data)[["NegGeoMean"]]
for(ann in negCols) {
plt <- QC_histogram(pData(Data), ann, col_by, 2, scale_trans = "log10")
print(plt)
}

# Detatch neg_geomean columns ahead of aggregateCounts call
pData(Data) <- pData(Data)[, !colnames(pData(Data)) %in% negCols]
Show all NTC values, Freq = # of Segments with a given NTC count:
QC<-sData(Data)
ntc_df <-QC[,c("slide_name","Plate_ID","NTC_ID","NTC")]
temptable<-ntc_df %>% dplyr::count(ntc_df$slide_name,ntc_df$NTC_ID,ntc_df$Plate_ID,ntc_df$NTC)
colnames(temptable) <- c("Slide_name","NTC_ID","Plate_ID","NTC_count","Number_of_samples")
datatable(temptable, rownames = FALSE)
kable(table(NTC_Count = sData(Data)$NTC), col.names = c("NTC Count", "# of Segments"))
| 3 |
64 |
| 113 |
71 |
| 397 |
47 |
| 8704 |
94 |
kable(QC_Summary, caption = "QC Summary Table for each Segment")
QC Summary Table for each Segment
| LowReads |
272 |
4 |
| LowTrimmed |
276 |
0 |
| LowStitched |
273 |
3 |
| LowAligned |
266 |
10 |
| LowSaturation |
272 |
4 |
| LowNegatives |
276 |
0 |
| HighNTC |
276 |
0 |
| LowNuclei |
276 |
0 |
| LowArea |
265 |
11 |
| TOTAL FLAGS |
259 |
17 |
datatable(QC_Summary,
caption = "AOI QC Summary",
extensions = 'Buttons', options = list (
dom = 'Bftrip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
)
)
Show AOIs which fail critical QCs.
QC<-sData(Data)
undersat<-subset(QC, `Saturated (%)`<= QC_params$percentSaturation)
if(nrow(undersat)> 0) {
datatable(aggregate(undersat, by=list(undersat$SampleID),paste,collapse=";"),
extensions = 'Buttons', options = list (
dom = 'Bftrip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
)
)}
Subsetting our dataset has removed samples which did not pass QC
Data <- Data[, QCResults$QCStatus == "PASS"]
Generally keep the qcCutoffs parameters unchanged. Set
removeLocalOutliers to FALSE if you do not want to remove local
outliers
Data <- setBioProbeQCFlags(Data,
qcCutoffs = list(minProbeRatio = 0.1,
percentFailGrubbs = 20),
removeLocalOutliers = FALSE)
ProbeQCResults <- fData(Data)[["QCFlags"]]
Define QC table for Probe QC
qc_df <- data.frame(Passed = sum(rowSums(ProbeQCResults[, -1]) == 0),
Global = sum(ProbeQCResults$GlobalGrubbsOutlier),
Local = sum(rowSums(ProbeQCResults[, -2:-1]) > 0
& !ProbeQCResults$GlobalGrubbsOutlier))
Subset object to exclude all that did not pass Ratio & Global
testing
ProbeQCPassed <-
subset(Data,
fData(Data)[["QCFlags"]][,c("LowProbeRatio")] == FALSE &
fData(Data)[["QCFlags"]][,c("GlobalGrubbsOutlier")] == FALSE)
Data <- ProbeQCPassed
cat("After QC features:", dim(Data)[1], "\nAfter QC samples:", dim(Data)[2])
## After QC features: 18641
## After QC samples: 259
Check how many unique targets the object has
length(unique(featureData(Data)[["TargetName"]]))
## [1] 18504
Collapse to targets
target_Data <- aggregateCounts(Data)
exprs(target_Data)[1:5, 1:2]
## DSP-1001250007851-H-A02.dcc DSP-1001250007851-H-A03.dcc
## A2M 485 262
## NAT2 15 18
## ACADM 31 15
## ACADS 27 17
## ACAT1 29 24
Define LOQ SD threshold and minimum value
cutoff <- 2
minLOQ <- 2
4.4 Limit of Quantification
We define a limit of quantification (LOQ) per ROI/AOI segment based
on the negative control probes to guide the filtering of segments and
genes with low signal relative to background. The formula for
calculating the LOQ in the \(i^{th}\)
segment at \(n\) standard deviations
(\(n = 2\) for this study) is: \(LOQ_i=geomean(NegProbe_i)*geoSD(NegProbe_i)^n\)
Calculate LOQ per module tested
LOQ <- data.frame(row.names = colnames(target_Data))
for(module in modules) {
vars <- paste0(c("NegGeoMean_", "NegGeoSD_"),
module)
if(all(vars[1:2] %in% colnames(pData(target_Data)))) {
LOQ[, module] <-
pmax(minLOQ,
pData(target_Data)[, vars[1]] *
pData(target_Data)[, vars[2]] ^ cutoff)
}
}
pData(target_Data)$LOQ <- LOQ
4.5 Filtering
After determining the limit of quantification (LOQ) per segment, we
recommend filtering out either segments and/or genes with abnormally low
signal. Filtering is an important step to focus on the true biological
data of interest.
We determine the number of genes detected in each segment across the
dataset.
LOQ_Mat <- c()
for(module in modules) {
ind <- fData(target_Data)$Module == module
Mat_i <- t(esApply(target_Data[ind, ], MARGIN = 1,
FUN = function(x) {
x > LOQ[, module]
}))
LOQ_Mat <- rbind(LOQ_Mat, Mat_i)
}
# ensure ordering since this is stored outside of the geomxSet
LOQ_Mat <- LOQ_Mat[fData(target_Data)$TargetName, ]
4.5.1 Segment Gene Detection
We first filter out segments with exceptionally low signal. These
segments will have a small fraction of panel genes detected above the
LOQ relative to the other segments in the study. Let’s visualize the
distribution of segments with respect to their % genes detected:
Save detection rate information to pheno data
pData(target_Data)$GenesDetected <-
colSums(LOQ_Mat, na.rm = TRUE)
pData(target_Data)$GeneDetectionRate <-
pData(target_Data)$GenesDetected / nrow(target_Data)
Determine detection thresholds: 1%, 5%, 10%, 15%, >15%
pData(target_Data)$DetectionThreshold <-
cut(pData(target_Data)$GeneDetectionRate,
breaks = c(0, 0.01, 0.05, 0.1, 0.15, 0.2,1),
labels = c("<1%", "1-5%", "5-10%", "10-15%", "15-20%", ">20%"))
# stacked bar plot of different cut points (1%, 5%, 10%, 15%)
ggplot(pData(target_Data),
aes(x = DetectionThreshold)) +
geom_bar(aes(fill = ANN2)) +
geom_text(stat = "count", aes(label = ..count..), vjust = -0.5) +
theme_bw() +
scale_y_continuous(expand = expansion(mult = c(0, 0.1))) +
scale_fill_manual(values=color_list$ANN2) +
labs(x = "Gene Detection Rate",
y = "Segments, #",
fill = "Segment Type")

cut percent genes detected at 1, 5, 10, 15
kable(table(pData(target_Data)$DetectionThreshold,
pData(target_Data)$ANN2))
| <1% |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
| 1-5% |
0 |
1 |
0 |
0 |
6 |
0 |
0 |
| 5-10% |
8 |
3 |
0 |
1 |
5 |
1 |
0 |
| 10-15% |
13 |
11 |
1 |
3 |
2 |
3 |
0 |
| 15-20% |
7 |
5 |
3 |
7 |
5 |
10 |
0 |
| >20% |
5 |
11 |
55 |
13 |
15 |
41 |
23 |
# set threshold for detectionlevel
# default 0.1
gene_det_threshold <- 0.05
target_Data <-
target_Data[, pData(target_Data)$GeneDetectionRate >= gene_det_threshold]
dim(target_Data)
## Features Samples
## 18504 251
4.6 Manual removal of samples/classes
active_aois<-names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))
re-Collect annotations
# gather the data and plot in order: class, slide name, region, segment
#count_mat <- dplyr::count(pData(Data), ANN1,ANN2,ANN3,ANN4,slide_name)
temp_qc <- temp_df
temp_qc$QCResult <- QCResults$QCStatus
temp_qc$QCResult[temp_qc$QCResult == "WARNING"] <- "X"
#count_mat <- dplyr::count(a, ANN1, ANN2, slide_name, QCResult)
count_mat <- temp_qc %>% dplyr::count(temp_qc[c(ann_names, "QCResult")])
test_gr <- gather_set_data(count_mat, 1:(ann_size+1))
test_gr$x <- factor(test_gr$x, labels = c(ann_names, "QCResult"))
re-Plot Sankey
ggplot(test_gr, aes(x, id = id, split = y, value = n)) +
geom_parallel_sets(aes(fill = ANN2), alpha = 0.5, axis.width = 0.1) +
geom_parallel_sets_axes(axis.width = 0.2) +
geom_parallel_sets_labels(color = "white", size = 5) +
theme_classic(base_size = 17) +
theme(legend.position = "bottom",
axis.ticks.y = element_blank(),
axis.line = element_blank(),
axis.text.y = element_blank()) +
scale_y_continuous(expand = expansion(0)) +
scale_x_discrete(expand = expansion(0)) +
scale_fill_manual(values=color_list$ANN2) +
labs(x = "", y = "") +
annotate(geom = "segment", x = 3.25, xend = 3.25, y = 10,
yend = 60, lwd = 2) +
annotate(geom = "text", x = 3.19, y = 25, angle = 90, size = 5,
hjust = 0.5, label = "50 segments")

4.7 Gene Detection Rate
Calculate detection rate
LOQ_Mat <- LOQ_Mat[, colnames(target_Data)]
fData(target_Data)$DetectedSegments <- rowSums(LOQ_Mat, na.rm = TRUE)
fData(target_Data)$DetectionRate <-
fData(target_Data)$DetectedSegments / nrow(pData(target_Data))
Gene of interest detection table
goi <- c("PDCD1", "CD274", "IFNG", "CD8A", "CD68", "EPCAM",
"KRT18", "NPHS1", "NPHS2", "CALB1", "CLDN8")
goi_df <- data.frame(
Gene = goi,
Number = fData(target_Data)[goi, "DetectedSegments"],
DetectionRate = percent(fData(target_Data)[goi, "DetectionRate"]))
4.8 Gene Filtering
We will graph the total number of genes detected in different
percentages of segments. Based on the visualization below, we can better
understand global gene detection in our study and select how many low
detected genes to filter out of the dataset. Gene filtering increases
performance of downstream statistical tests and improves interpretation
of true biological signal.
Plot detection rate
plot_detect <- data.frame(Freq = c(1, 5, 10, 20, 30, 50))
plot_detect$Number <-
unlist(lapply(c(0.01, 0.05, 0.1, 0.2, 0.3, 0.5),
function(x) {sum(fData(target_Data)$DetectionRate >= x)}))
plot_detect$Rate <- plot_detect$Number / nrow(fData(target_Data))
rownames(plot_detect) <- plot_detect$Freq
ggplot(plot_detect, aes(x = as.factor(Freq), y = Rate, fill = Rate)) +
geom_bar(stat = "identity") +
geom_text(aes(label = formatC(Number, format = "d", big.mark = ",")),
vjust = 1.6, color = "black", size = 4) +
scale_fill_gradient2(low = "orange2", mid = "lightblue",
high = "dodgerblue3", midpoint = 0.65,
limits = c(0,1),
labels = scales::percent) +
theme_bw() +
scale_y_continuous(labels = scales::percent, limits = c(0,1),
expand = expansion(mult = c(0, 0))) +
labs(x = "% of Segments",
y = "Genes Detected, % of Panel > LOQ")

Subset to target genes detected in at least 10% of the samples. Also
manually include the negative control probe, for downstream use
# default=0.1
negativeProbefData <- subset(fData(target_Data), CodeClass == "Negative")
neg_probes <- unique(negativeProbefData$TargetName)
target_Data <-
target_Data[fData(target_Data)$DetectionRate >= 0.05 |
fData(target_Data)$TargetName %in% neg_probes, ]
# retain only detected genes of interest
goi <- goi[goi %in% rownames(target_Data)]
dim(target_Data)
## Features Samples
## 12241 251
5 Normalization
We will now normalize the GeoMx data for downstream visualizations
and differential expression. The two common methods for normalization of
DSP-NGS RNA data are i) quartile 3 (Q3) or ii) background
normalization.
Both of these normalization methods estimate a normalization factor
per segment to bring the segment data distributions together. More
advanced methods for normalization and modeling are under active
development. However, for most studies, these methods are sufficient for
understanding differences between biological classes of segments and
samples.
Q3 normalization is typically the preferred normalization strategy
for most DSP-NGS RNA studies. Given the low negative probe counts in
this particular dataset as shown during Segment QC, we would further
avoid background normalization as it may be less stable.
Before normalization, we will explore the relationship between the
upper quartile (Q3) of the counts in each segment with the geometric
mean of the negative control probes in the data. Ideally, there should
be a separation between these two values to ensure we have stable
measure of Q3 signal. If you do not see sufficient separation between
these values, you may consider more aggressive filtering of low signal
segments/genes.
Graph Q3 value vs negGeoMean of Negatives
ann_of_interest <- "ANN2"
Stat_data <-
data.frame(row.names = colnames(exprs(target_Data)),
Segment = colnames(exprs(target_Data)),
Annotation = pData(target_Data)[, ann_of_interest],
Q3 = unlist(apply(exprs(target_Data), 2,
quantile, 0.75, na.rm = TRUE)),
NegProbe = exprs(target_Data)[neg_probes, ])
Stat_data_m <- melt(Stat_data, measure.vars = c("Q3", "NegProbe"),
variable.name = "Statistic", value.name = "Value")
plt1 <- ggplot(Stat_data_m,
aes(x = Value, fill = Statistic)) +
geom_histogram(bins = 40) + theme_bw() +
scale_x_continuous(trans = "log2") +
facet_wrap(~Annotation, nrow = 1) +
scale_fill_brewer(palette = 3, type = "qual") +
labs(x = "Counts", y = "Segments, #")
plt2 <- ggplot(Stat_data,
aes(x = NegProbe, y = Q3, color = Annotation)) +
geom_abline(intercept = 0, slope = 1, lty = "dashed", color = "darkgray") +
geom_point() + guides(color = "none") + theme_bw() +
scale_x_continuous(trans = "log2") +
scale_y_continuous(trans = "log2") +
theme(aspect.ratio = 1) +
labs(x = "Negative Probe GeoMean, Counts", y = "Q3 Value, Counts")
plt3 <- ggplot(Stat_data,
aes(x = NegProbe, y = Q3 / NegProbe, color = Annotation)) +
geom_hline(yintercept = 1, lty = "dashed", color = "darkgray") +
geom_point() + theme_bw() +
scale_x_continuous(trans = "log2") +
scale_y_continuous(trans = "log2") +
theme(aspect.ratio = 1) +
labs(x = "Negative Probe GeoMean, Counts", y = "Q3/NegProbe Value, Counts")
btm_row <- plot_grid(plt2, plt3, nrow = 1, labels = c("B", ""),
rel_widths = c(0.43,0.57))
plot_grid(plt1, btm_row, ncol = 1, labels = c("A", ""))

Q3 norm (75th percentile) for WTA/CTA with or without custom
spike-ins
target_Data <- normalize(target_Data ,
norm_method = "quant",
desiredQuantile = .75,
toElt = "q_norm")
#, data_type = "RNA" depricated after 4.1
Quantile Normalization
quantile <- normalize.quantiles(target_Data@assayData[["exprs"]])
rownames(quantile) <- rownames(target_Data@assayData[["exprs"]])
colnames(quantile) <- colnames(target_Data@assayData[["exprs"]])
assayDataElement(object = target_Data, elt = "quantile_norm") <- quantile
Background normalization for WTA/CTA without custom spike-in
target_Data <- normalize(target_Data,
norm_method = "neg",
fromElt = "exprs",
toElt = "neg_norm")
# , data_type = "RNA" depricated after 4.1
5.1 Visualize the first 10 segments with
each normalization method
Use the tab-menu to navigate!
#Fix zero values, which go to -inf in log transform in standard boxplot
# temp <-as.matrix(exprs((target_Data)[,1:10]))
# long <- melt(temp)
# colnames(long) <- c("gene","segment","count")
# ggplot(long, aes(x=segment,y=count)) +
# geom_boxplot(fill="#9EDAE5") +
# scale_y_continuous(trans=scales::pseudo_log_trans(base = 10)) +
# scale_x_discrete(labels=c(1:10)) +
# labs(title="Raw counts", x="segment", y = "Counts, Raw")
#
#
# temp <-as.matrix(assayDataElement(target_Data[,1:10], elt = "q_norm"))
# long <- melt(temp)
# colnames(long) <- c("gene","segment","count")
# ggplot(long, aes(x=segment,y=count)) +
# geom_boxplot(fill = "#2CA02C") +
# scale_y_continuous(trans=scales::pseudo_log_trans(base = 10)) +
# scale_x_discrete(labels=c(1:10)) +
# labs(title="Q3 Norm Counts", x="segment", y = "Counts, Q3 Normalized")
#
#
# temp <-as.matrix(assayDataElement(target_Data[,1:10], elt = "neg_norm"))
# long <- melt(temp)
# colnames(long) <- c("gene","segment","count")
# ggplot(long, aes(x=segment,y=count)) +
# geom_boxplot(fill = "#FF7F0E") +
# scale_y_continuous(trans=scales::pseudo_log_trans(base = 10)) +
# scale_x_discrete(labels=c(1:10)) +
# labs(title="Neg Norm Counts", x="segment", y = "Counts, Neg. Normalized")
raw counts
boxplot(exprs(target_Data)[,1:8],
col = "#9EDAE5", main = "Raw Counts",
log = "y", names = 1:8, xlab = "Segment",
ylab = "Counts, Raw")

Q3 normalized
boxplot(assayDataElement(target_Data[,1:8], elt = "q_norm"),
col = "#2CA02C", main = "Q3 Norm Counts",
log = "y", names = 1:8, xlab = "Segment",
ylab = "Counts, Q3 Normalized")

Quantile normalized (testing phase)
boxplot(assayDataElement(target_Data[,1:8], elt = "quantile_norm"),
col = "pink", main = "Quantile Norm Counts",
log = "y", names = 1:8, xlab = "Segment",
ylab = "Counts, Quantile Normalized")

Negative probe normalization
boxplot(assayDataElement(target_Data[,1:8], elt = "neg_norm"),
col = "#FF7F0E", main = "Neg Norm Counts",
log = "y", names = 1:8, xlab = "Segment",
ylab = "Counts, Neg. Normalized")

6 Unsupervised Analysis
6.1 UMAP
Use the tab-menu to navigate!
1
custom_umap <- umap::umap.defaults
custom_umap$random_state <- 42
# run UMAP
umap_out <-
umap(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
config = custom_umap)
pData(target_Data)[, c("UMAP1", "UMAP2")] <- umap_out$layout[, c(1,2)]
ggplot(pData(target_Data),
aes(x = UMAP1, y = UMAP2, color = slide_name, shape = ANN1)) +
geom_point(size = 3) +
#geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
theme_bw()

batch_correction_needed = FALSE
2
ggplot(pData(target_Data),
aes(x = UMAP1, y = UMAP2, color = ANN3, shape = ANN1)) +
geom_point(size = 3) +
#geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
theme_bw()

6.2 Batch correction
If the data is observed to have a batch effect it can be corrected
with the methods: RUV4, LIMMA, or Combat-Seq. Each method is done and
the best one is picked and used.
The standR package is short for Spatial transcriptomics analyzes and
decoding in R, it aims at providing good practice pipeline and useful
functions for users to analyze Nanostring’s GeoMx DSP data. In the
Nanostring’s GeoMX DSP protocol, due to the fact that one slide is only
big enough for a handful of tissue segments (ROIs), it is common that we
see the DSP data being confounded by the batch effect introduced by
different slides. In order to establish fair comparison between ROIs
later on, it is necessary to remove this batch effect from the data. (https://bioconductor.org/packages/release/bioc/vignettes/standR/inst/doc/standR_introduction.html)
For RUV4 correction, the function is requiring 3 parameters other
than the input object, including factors: the factor of interest,
i.e. the biological variation we plan to keep; NCGs: the list of
negative control genes detected using the function findNCGs; and k: is
the number of unwanted factors to use, in the RUV documentation, it is
suggest that we should use the smallest k once we don’t observe
technical variation in the data.
Another option is set the parameter method to “Limma”, which uses the
remove batch correction method from limma. In this mode, the function is
requiring 2 parameters, including batch: a vector that indicating
batches for all samples; and design: a design matrix which is generated
by model.matrix, in the design matrix, all biologically-relevant factors
should be included.
ComBat-seq is a batch effect adjustment tool for bulk RNA-seq count
data. It is an improved model based on the popular ComBat, to address
its limitations through novel methods designed specifically for RNA-Seq
studies. ComBat-seq takes untransformed, raw count matrix as input. Same
as ComBat, it requires a known batch variable. (https://github.com/zhangyuqing/ComBat-seq)
Set up standR object
count_geomx <- as.data.frame(target_Data@assayData[["exprs"]])
sample_geomx <- target_Data@phenoData@data
sample_geomx$roi <- target_Data@protocolData@data$roi
sample_geomx <- as.data.frame(sample_geomx)
sample_geomx$Sample_ID <- rownames(sample_geomx)
sample_geomx$SegmentDisplayName <- paste(sample_geomx$`scan name`, sample_geomx$roi, sample_geomx$segment, sep = " | ")
sample_geomx$ROICoordinateX <- 1
sample_geomx$ROICoordinateY <- 1
feature_geomx <- fData(Data)
feature_geomx <- feature_geomx[c("RTS_ID", "TargetName", "ProbeID", "Negative")]
feature_geomx <- as.data.frame(feature_geomx)
rownames(feature_geomx) <- NULL
matching <- sample_geomx$SegmentDisplayName[sample_geomx$Sample_ID %in% colnames(count_geomx)]
colnames(count_geomx) <- matching
count_geomx$TargetName <- rownames(count_geomx)
rownames(count_geomx) <- NULL
spe <- readGeoMx(count_geomx, sample_geomx, featureAnnoFile = feature_geomx, hasNegProbe = TRUE)
colData(spe)$regions <- paste0(colData(spe)$ANN2,"_",colData(spe)$ANN1) |>
(\(.) gsub("_Geometric Segment","",.))() |>
paste0("_",colData(spe)$pathology) |>
(\(.) gsub("_NA","_ns",.))()
colData(spe)$regions <- paste0(colData(spe)$ANN2,"_",colData(spe)$ANN1) |>
(\(.) gsub("_Geometric Segment","",.))() |>
paste0("_",colData(spe)$pathology) |>
(\(.) gsub("_NA","_ns",.))()
colData(spe)$biology <- paste0(colData(spe)$regions)
See optimal k value for RUV4
spe <- findNCGs(spe, batch_name = "slide_name", top_n = 500)
findBestK(spe, maxK = 10, factor_of_int = "biology", NCGs = metadata(spe)$NCGs, factor_batch = "slide_name")
RUV4
ruv4 <- geomxBatchCorrection(spe, factors = "biology",
NCGs = metadata(spe)$NCGs, k = 1)
plotPairPCA(ruv4, assay = 2, color = slide_name, shape = regions, title = "RUV4 removeBatch")
plotRLExpr(ruv4, assay = 2, color = `slide_name`) + ggtitle("RUV4 removeBatch")
Limma
limma <- geomxBatchCorrection(spe,
batch = colData(spe)$`slide_name`, method = "Limma",
design = model.matrix(~ 0 + ANN1 + regions,
data = colData(spe)))
plotPairPCA(limma, assay = 2, color = slide_name, shape = regions, title = "Limma removeBatch")
plotRLExpr(limma, assay = 2, color = slide_name) + ggtitle("Limma removeBatch")
CombatSeq
adjusted <- ComBat_seq(target_Data@assayData[["exprs"]], batch=target_Data@phenoData@data[["slide_name"]], group=target_Data@phenoData@data[["ANN2"]])
assayDataElement(object = target_Data, elt = "combat") <- adjusted
umap_out2 <-
umap(t(log2(assayDataElement(target_Data , elt = "combat"))),
config = custom_umap)
pData(target_Data)[, c("UMAP1", "UMAP2")] <- umap_out2$layout[, c(1,2)]
ggplot(pData(target_Data),
aes(x = UMAP1, y = UMAP2, color = slide_name, shape = ANN1)) +
geom_point(size = 3) +
#geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
theme_bw() + ggtitle("CombatSeq removeBatch")
Compare limma vs RUV4
spe_list <- list(spe, ruv4, limma)
plotClusterEvalStats(spe_list = spe_list,
bio_feature_name = "regions",
batch_feature_name = "slide_name",
data_names = c("Raw","RUV4","Limma"))
Add batch correction result to target_Data
neg_probes_save <- t(as.matrix(target_Data@assayData$q_norm["NegProbe-WTX",]))
rownames(neg_probes_save) <- "NegProbe-WTX"
# Depending on correct method, change the word "limma" to "ruv4" or vice versa.
limma <-limma@assays@data@listData[["logcounts"]]
limma <- rbind(limma, neg_probes_save)
colnames(limma) <- colnames(as.data.frame(target_Data@assayData[["q_norm"]]))
assayDataElement(object = target_Data, elt = "limma") <- limma
ruv4 <-ruv4@assays@data@listData[["logcounts"]]
ruv4 <- rbind(ruv4, neg_probes_save)
colnames(ruv4) <- colnames(as.data.frame(target_Data@assayData[["q_norm"]]))
assayDataElement(object = target_Data, elt = "ruv4") <- ruv4
Choose method
# choose method to replace q_norm or set it to ""
method <- ""
# replace q_norm with chosen method
if (!method == "") {
# save orginal q_norm
assayDataElement(object = target_Data, elt = "original") <- assayDataElement(object = target_Data, elt = "q_norm")
assayDataElement(object = target_Data, elt = "q_norm") <- assayDataElement(object = target_Data, elt = method)
print(paste("Batch correction method:", method))
} else {print("No batch correction needed")}
## [1] "No batch correction needed"
Umap after batch correction
umap_out <-
umap(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
config = custom_umap)
pData(target_Data)[, c("UMAP1", "UMAP2")] <- umap_out$layout[, c(1,2)]
ggplot(pData(target_Data),
aes(x = UMAP1, y = UMAP2, color = slide_name, shape = ANN1)) +
geom_point(size = 3) +
#geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
theme_bw()
6.3 Run tSNE
Use the tab-menu to navigate!
One common approach to understanding high-plex data is dimension
reduction. Two common methods are UMAP and tSNE, which are
non-orthogonally constrained projections that cluster samples based on
overall gene expression. In this study, we see by either UMAP (from the
umap package) or tSNE (from the Rtsne package)
1
tsne_out <-
Rtsne(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
perplexity = ncol(target_Data)*.15)
pData(target_Data)[, c("tSNE1", "tSNE2")] <- tsne_out$Y[, c(1,2)]
ggplot(pData(target_Data),
aes(x = tSNE1, y = tSNE2, shape = slide_name, color = ANN2)) +
geom_point(size = 3) +
#geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
theme_bw()

2
tsne_out <-
Rtsne(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
perplexity = ncol(target_Data)*.15)
pData(target_Data)[, c("tSNE1", "tSNE2")] <- tsne_out$Y[, c(1,2)]
ggplot(pData(target_Data),
aes(x = tSNE1, y = tSNE2, color = ANN3, shape = ANN1)) +
geom_point(size = 3) +
#geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
theme_bw()

6.4 Clustering high CV Genes
Another approach to explore the data is to calculate the coefficient
of variation (\(CV\)) for each gene
(\(g\)) using the formula \(CV_g=SD_g/mean_g\). We then identify genes
with high CVs that should have large differences across the various
profiled segments. This unbiased approach can reveal highly variable
genes across the study.
We plot the results using unsupervised hierarchical clustering,
displayed as a heatmap.
# create a log2 transform of the data for analysis
assayDataElement(object = target_Data, elt = "log_q") <-
assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")
# create CV function
calc_CV <- function(x) {sd(x) / mean(x)}
CV_dat <- assayDataApply(target_Data,
elt = "log_q", MARGIN = 1, calc_CV)
# show the highest CD genes and their CV values
sort(CV_dat, decreasing = TRUE)[1:5]
## CAMK2N1 AKR1C1 AQP2 REN GDF15
## 0.6344192 0.5238995 0.4752667 0.4414027 0.4276390
Table of CV values
# show the highest CD genes and their CV values
datatable(as.data.frame(CV_dat),
extensions = 'Buttons', options = list (
order = list(1, 'desc'),
dom = 'Bftrip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
), caption = "CV values of genes"
) %>% formatRound(columns=c("CV_dat"), digits=3)
Heatmap genes in the top 3rd of the CV values
GOI <- names(CV_dat)[CV_dat > quantile(CV_dat, 0.75)]
pheatmap(assayDataElement(target_Data[GOI, ], elt = "log_q"),
scale = "row",
cutree_cols = 3,
cutree_rows = 2,
show_rownames = FALSE, show_colnames = TRUE,
border_color = NA,
drop_levels = TRUE,
clustering_method = "average",
clustering_distance_rows = "correlation",
clustering_distance_cols = "correlation",
breaks = seq(-3, 3, 0.05),
color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
annotation_colors = color_list,
annotation_col = pData(target_Data)[, ann_names])

assayDataElement(object = target_Data, elt = "log_q") <- assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")
log_q <-as.data.frame(assayDataElement(target_Data, elt= "log_q"))
#batch <-as.data.frame(assayDataElement(target_Data, elt= "batch"))
6.5.0 Create subset of data
# determine AOIs to use
#active_aois<-rownames(ann)[ann$patient=="p4"]
active_aois<- names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))
6.5.1 Clustering high CV genes for
subset
Calculating CV values
# create a log2 transform of the data for analysis
assayDataElement(object = target_Data, elt = "log_q") <-
assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")
# create CV function
calc_CV <- function(x) {sd(x) / mean(x)}
CV_dat <- assayDataApply(target_Data[,active_aois],
elt = "log_q", MARGIN = 1, calc_CV)
Table of CV values
# show the highest CD genes and their CV values
datatable(as.data.frame(CV_dat),
extensions = 'Buttons', options = list (
order = list(1, 'desc'),
dom = 'Bftrip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
), caption = "CV values of genes"
) %>% formatRound(columns=c("CV_dat"), digits=3)
Heatmap on of subset, genes in the top 3rd of the CV
values
# Identify genes in the top 3rd of the CV values
GOI <- names(CV_dat)[CV_dat > quantile(CV_dat, 0.75)]
pheatmap(assayDataElement(target_Data[GOI,active_aois ], elt = "log_q"),
scale = "row",
fontsize_row = 5,
cutree_cols = 3,
cutree_rows = 2,
show_rownames = FALSE, show_colnames = TRUE,
border_color = NA,
clustering_method = "average",
clustering_distance_rows = "correlation",
clustering_distance_cols = "correlation",
breaks = seq(-3, 3, 0.05),
color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
annotation_colors = color_list,
annotation_col =
pData(target_Data)[, ann_names])

7.1 Differential Expression

t-test
#gc()
plots<-list()
tables<-list()
labels<-list()
test<-"ttest"
mtc<-"BY"
#options: "holm" "hochberg" "hommel" "bonferroni" "BH" "BY" "fdr"
counter=1
comps_df<-data.frame(comp='',val='')
for(region in unique(pData(target_Data)$ANN3)) {
for (active_group1 in unique(pData(target_Data)$ANN1)) {
for (active_group2 in unique(pData(target_Data)$ANN1)) {
#supress reduncant compares
if(active_group1==active_group2) {next}
comp<-paste(sort(c(region, active_group1,active_group2)),collapse = "_")
if(comp %in% comps_df$comp) {next}
temp_df<-data.frame(comp=comp ,val=1)
comps_df<-rbind(comps_df,temp_df)
labels[[counter]]<-paste(active_group1," vs ", active_group2)
group1<-log_q[,names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))[pData(target_Data)$ANN1==active_group1 & pData(target_Data)$ANN3==region]]
group2<-log_q[,names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))[pData(target_Data)$ANN1==active_group2 & pData(target_Data)$ANN3==region]]
#run t_tests
results<-as.data.frame ( apply(log_q, 1, function(x) t.test(x[colnames(group1)],x[colnames(group2)])$p.value) )
colnames(results)<-"raw_p_value"
#multiple_testing_correction
adj_p_value<- p.adjust(results$raw_p_value,method=mtc)
results<-cbind(results,adj_p_value)
#calc_fdr
FDR<- p.adjust(results$raw_p_value,method="fdr")
results<-cbind(results,FDR)
#fold_changes
#as base data is already log transformed, means need to be subtracted to get FC in log space
fchanges<-as.data.frame(apply(log_q, 1, function(x) (mean(x[colnames(group1)]) - mean(x[colnames(group2)]))))
colnames(fchanges)<-"FC"
#paste("FC",active_group1," / ",active_group2)
results<-cbind(results,fchanges)
#add genenames
results$Gene<-rownames(results)
#set categories based on P-value & FDR for plotting
results$Color <- "NS or FC < 1"
results$Color[results$raw_p_value < 0.05] <- "P < 0.05"
results$Color[results$FDR < 0.05] <- "FDR < 0.05"
results$Color[results$FDR < 0.001] <- "FDR < 0.001"
results$Color[abs(results$FC) <1] <- "NS or FC < 1"
results$Color <- factor(results$Color,
levels = c("NS or FC < 1", "P < 0.05", "FDR < 0.05", "FDR < 0.001"))
#vulcanoplot
# pick top genes for either side of volcano to label
# order genes for convenience:
results$invert_P <- (-log10(results$adj_p_value)) * sign(results$FC)
top_g <- c()
top_g <- c(top_g,
results[, 'Gene'][
order(results[, 'invert_P'], decreasing = TRUE)[1:20]],
results[, 'Gene'][order(results[, 'invert_P'], decreasing = FALSE)[1:20]])
top_g<- unique(top_g)
results <- results[, -1*ncol(results)] # remove invert_P from matrix
# Graph results
plots[[counter]]<- ggplot(results,
aes(x = FC, y = -log10(raw_p_value),
color = Color, label = Gene)) +
geom_vline(xintercept = c(1, -1), lty = "dashed") +
geom_hline(yintercept = -log10(0.05), lty = "dashed") +
geom_point() +
labs(x = paste("Enriched in", active_group2," <- log2(FC) -> Enriched in", active_group1),
y = "Significance, -log10(P)",
color = "Significance") +
scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
`FDR < 0.05` = "lightblue",
`P < 0.05` = "orange2",
`NS or FC < 0.5` = "gray"),
guide = guide_legend(override.aes = list(size = 4))) +
scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
geom_text_repel(data = subset(results, (-1>FC| FC>1) & FDR < 0.05 & Gene %in% top_g),
point.padding = 0.15, color = "black", size=3.5,
min.segment.length = .1, box.padding = .2, lwd = 2,
max.overlaps = 50) +
theme_bw(base_size = 10) +
theme(legend.position = "bottom") +
ggtitle(paste("class: ", region," - ", test, mtc,"multitest corr"))
#store tables for display later
tables[[counter]]<-results
counter = counter+1
#datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
}
}
}
grid.arrange(grobs=plots,ncol=2)

#strangly does not appear in html output -> chucks are limited to one datatable per chuck. The htmltools are a way around this but does show them stacked in a box.
results_tables <- htmltools::tagList()
for (c in (2:counter-1)) {
#Gene %in% GOI
results_tables[[c]] <- datatable(subset(tables[[c]], Color == c("FDR < 0.001", "P < 0.05")),
rownames=FALSE,
extensions = c('Buttons'), options = list (
dom = 'Bftrip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
), height = 550,
caption = paste("DE results ", labels[[c]]),filter='top') %>% formatRound(columns=c("raw_p_value","adj_p_value","FDR","FC"), digits=3)
#cat('\n\n<!-- -->\n\n')
}
results_tables
print("") # for layout of next section
## [1] ""
7.2 DE analysis with LMM
A common statistical approach is to use a linear mixed-effect model
(LMM). The LMM allows the user to account for the subsampling per
tissue; in other words, we adjust for the fact that the multiple regions
of interest placed per tissue section are not independent observations,
as is the assumption with other traditional statistical tests. The
formulation of the LMM model depends on the scientific question being
asked.
Overall, there are two flavors of the LMM model when used with GeoMx
data: i) with and ii) without random slope.
When comparing features that co-exist in a given tissue section, a
random slope is included in the LMM model. When comparing features that
are mutually exclusive in a given tissue section the LMM model does not
require a random slope.
Mostly, we use patient/sample as Random Intercept when they are
combined on slides.

# convert test variables to factors
pData(target_Data)$testClass <- factor(pData(target_Data)$ANN1, unique(target_Data$ANN1))
pData(target_Data)[["slide"]] <- factor(pData(target_Data)[["slide_name"]])
#assayDataElement(object = target_Data, elt = "log_q") <- assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")
# run LMM:
# formula follows conventions defined by the lme4 package
lmm_results <- c()
for (region in unique(pData(target_Data)$ANN3)) {
ind <- pData(target_Data)$ANN3 == region
mixedOutmc <-
mixedModelDE(target_Data[,ind],
elt = "log_q",
modelFormula = ~ testClass + (1 | slide),
groupVar = "testClass",
nCores = parallel::detectCores(),
multiCore = TRUE)
# format results as data.frame
r_test <- do.call(rbind, mixedOutmc["lsmeans", ])
tests <- rownames(r_test)
r_test <- as.data.frame(r_test)
r_test$Contrast <- tests
# use lapply in case you have multiple levels of your test factor to
# correctly associate gene name with it's row in the results table
r_test$Gene <-
unlist(lapply(colnames(mixedOutmc),
rep, nrow(mixedOutmc["lsmeans", ][[1]])))
r_test$Subset <- region
r_test$FDR <- p.adjust(r_test$`Pr(>|t|)`, method = "fdr")
r_test <- r_test[, c("Gene", "Subset", "Contrast", "Estimate",
"Pr(>|t|)", "FDR")]
lmm_results <- rbind(lmm_results, r_test)
}
7.3 Vulcanoplot + table of LMM
# Categorize Results based on P-value & FDR for plotting
fc_threshold = 1
lmm_results$Color <- paste("NS or FC < ",fc_threshold = 1)
lmm_results$Color[lmm_results$`Pr(>|t|)` < 0.05] <- "P < 0.05"
lmm_results$Color[lmm_results$FDR < 0.05] <- "FDR < 0.05"
lmm_results$Color[lmm_results$FDR < 0.001] <- "FDR < 0.001"
lmm_results$Color[abs(lmm_results$Estimate) < fc_threshold] <- paste("NS or FC < ",fc_threshold = 1)
lmm_results$Color <- factor(lmm_results$Color,
levels = c("NS or FC < 1", "P < 0.05",
"FDR < 0.05", "FDR < 0.001"))
counter = 1
plots_lmm <- list()
for(c in unique(lmm_results$Subset) ) {
lmm_results_slice = lmm_results[lmm_results$Subset == c,]
# pick top genes for either side of volcano to label
# order genes for convenience:
lmm_results_slice$invert_P <- (-log10(lmm_results_slice$`Pr(>|t|)`)) * sign(lmm_results_slice$Estimate)
#loop here over tested conditions if applicable
top_g <- c()
top_g <- c(top_g,
lmm_results_slice[, 'Gene'][
order(lmm_results_slice[, 'invert_P'], decreasing = TRUE)[1:15]],
lmm_results_slice[, 'Gene'][
order(lmm_results_slice[, 'invert_P'], decreasing = FALSE)[1:15]])
top_g <- unique(top_g)
lmm_results_slice <- lmm_results_slice[, -1*ncol(lmm_results_slice)] # remove invert_P from matrix
# Flip Contrast
contrast_lab <- as.character(lmm_results_slice$Contrast)
contrast_lab <- strsplit(contrast_lab, "-")[[1]]
contrast_lab <- paste(contrast_lab[2], "-", contrast_lab[1])
# Graph results
plots_lmm[[counter]] <- ggplot(lmm_results_slice,
aes(x = Estimate, y = -log10(`Pr(>|t|)`),
color = Color, label = Gene)) +
geom_vline(xintercept = c(fc_threshold, -fc_threshold), lty = "dashed") +
geom_hline(yintercept = -log10(0.05), lty = "dashed") +
geom_point() +
labs(
x = paste(contrast_lab, " log2(FC)"),
y = "Significance, -log10(P)",
color = "Significance") +
scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
`FDR < 0.05` = "lightblue",
`P < 0.05` = "orange2",
`NS or FC < 1` = "gray"),
guide = guide_legend(override.aes = list(size = 4))) +
scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
geom_text_repel(data = subset(lmm_results_slice, `Pr(>|t|)` < 0.001 & abs(lmm_results_slice$Estimate) > fc_threshold & Gene %in% top_g),
point.padding = 0.15, color = "black",size=5,
min.segment.length = .1, box.padding = .2, lwd = 2,
max.overlaps = 50) +
theme_bw(base_size = 16) +
theme(legend.position = "bottom") +
facet_wrap(~Subset, scales = "free_y")
counter <- counter + 1
}
grid.arrange(grobs=plots_lmm,ncol=2)

#subset(lmm_results, Gene %in% GOI)
datatable(lmm_results, rownames = FALSE,
extensions = 'Buttons', options = list (
dom = 'Bftrip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
),
caption = "DE results for Genes of Interest (>75% CV)",filter='top') %>% formatRound(columns=c("Estimate","Pr(>|t|)","FDR"), digits=3)
7.4 Plotting Genes of Interest
my_gois <-unique(subset(lmm_results, `FDR` < 0.001)$Gene)
tmp_tbl<-subset(lmm_results, Gene %in% my_gois)
my_gois <- c()
for (contrast in unique(tmp_tbl$Contrast)) { # gene of interest for all contrasts
#for (contrast in c("Jux_Glo - Pro_Tub")) {
ind <- tmp_tbl$Contrast == contrast
goi <- tmp_tbl[ind, "Gene"][order(tmp_tbl[ind, "Estimate"], decreasing = FALSE)[1:3]]
my_gois <- c(my_gois, goi)
goi <- tmp_tbl[ind, "Gene"][order(tmp_tbl[ind, "Estimate"], decreasing = TRUE)[1:3]]
my_gois <- c(my_gois, goi)
}
if(nrow(tmp_tbl) > 1) {
datatable(tmp_tbl,rownames = FALSE,caption = "DE results for Genes of Interest ",filter='top') %>% formatRound(columns=c("Estimate","Pr(>|t|)","FDR"), digits=3)
for (my_goi in my_gois) {
# show expression for a single target
a<-ggplot(pData(target_Data),
aes(x = ANN2, fill = ANN2,
y = as.numeric(assayDataElement(target_Data[my_goi, ], elt = "q_norm")))) +
geom_violin() +
geom_jitter(width = .2) +
labs(y = paste(my_goi,"Expression")) +
scale_y_continuous(trans = "log2") +
#facet_wrap(~ANN3, nrow=1) + theme_bw(base_size = 16) +
theme_bw() + coord_flip()
print(a)
}
}else{
print("No significant lMM results to plot")
}






7.5 Heatmap of Significant Genes
In addition to generating individual gene box plots or volcano plots,
we can again create a heatmap from our data. This time rather than
utilizing CV to select genes, we can use the P-value or FDR values to
select genes. Here, we plot all genes with an FDR < 0.001.
my_gois <-unique(subset(lmm_results, `FDR` < 0.001)$Gene) # 100 to prevent long runtime
if(length(my_gois)<2) {
print("No significant results to show")
}else{
pheatmap(log2(assayDataElement(target_Data[my_gois, ], elt = "q_norm")),
scale = "row",
show_rownames = TRUE, show_colnames = TRUE,
border_color = NA,
clustering_method = "average",
clustering_distance_rows = "correlation",
clustering_distance_cols = "correlation",
cutree_cols = 3, cutree_rows = 2,
breaks = seq(-3, 3, 0.05),
color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
annotation_colors = color_list,
annotation_col = pData(target_Data)[, ann_names])
}

8 Pathway Analysis
Pathway analysis enables exploration of different aggregate gene sets
for our experimental questions. Each individual ROI/AOI segment is
scored for every pathway of interest, which we can then use to
investigate biological differences. We will perform analogous analyses
as those outlined in the Differential Expression and Spatial
Deconvolution sections of the report for gene set enrichment.
8.1 Scoring Gene Sets
Pathways and gene sets were defined from the Kegg Brite database. We
use an R software package called Gene Set Variation Analysis to score
each segment within our study. see https://cran.r-project.org/web/packages/msigdbr/vignettes/msigdbr-intro.html
for options on collections. We use the KEGG and REACTOME.
#gc()
h_gene_sets = rbind(msigdbr(species = "human", subcategory = "CP:KEGG"),
msigdbr(species = "human", subcategory = "CP:REACTOME"))
#msigdbr(species = "human", subcategory = "CP:BIOCARTA")
msigdbr_list = split(x = h_gene_sets$gene_symbol, f = h_gene_sets$gs_name)
# prepare df for accurate merging back genes later
pw_gene_df<-data.frame(Pathway = names(msigdbr_list))
pw_gene_df$genes<-msigdbr_list
ssgsea_results <- GSVA::gsva(expr = assayDataElement(target_Data,
elt = "log_q"),
gset.idx.list = msigdbr_list,
method = "ssgsea",
min.sz = 5,
max.sz = 500,
verbose = FALSE)
## [1] "Calculating ranks..."
## [1] "Calculating absolute values from ranks..."
## [1] "Normalizing..."
geneSetObj <-
NanoStringGeoMxSet(assayData = ssgsea_results,
phenoData = AnnotatedDataFrame(pData(target_Data)),
protocolData = protocolData(target_Data),
featureType = "GeneSet",
check = FALSE)
8.2 Differental analysis of pathways
# # convert test variables to factors
pData(target_Data)$testClass <- factor(pData(target_Data)$ANN1, unique(target_Data$ANN1))
pData(geneSetObj)[["slide"]]<-factor(pData(geneSetObj)[["slide_name"]])
#pData(target_Data)$testRegion<-factor(pData(target_Data)$ANN2, unique(count_mat$ANN3))
lmm_ssgsea_results <- c()
for (region in unique(pData(target_Data)$ANN3)) {
ind <- pData(target_Data)$ANN3 == region
mixedOutmc <-
mixedModelDE(geneSetObj[, ind],
elt = "exprs",
modelFormula = ~ testClass + (1 | slide),
groupVar = "testClass",
nCores = parallel::detectCores(),
multiCore = TRUE)
# format results as data.frame
r_ssgsea_test <- do.call(rbind, mixedOutmc["lsmeans", ])
ssgsea_tests <- rownames(r_ssgsea_test)
r_ssgsea_test <- as.data.frame(r_ssgsea_test)
r_ssgsea_test$Contrast <- ssgsea_tests
#r_ssgsea_test$Genes <- msigdbr_list seems unreliable as gsva omits pathways if genes are not in data..
# use lapply in case you have multiple levels of your test factor to
# correctly associate gene name with it's row in the results table
r_ssgsea_test$Pathway <-
unlist(lapply(colnames(mixedOutmc),
rep, nrow(mixedOutmc["lsmeans", ][[1]])))
#r_ssgsea_test$Subset <- status
r_ssgsea_test$FDR <- p.adjust(r_ssgsea_test$`Pr(>|t|)`, method = "fdr")
r_ssgsea_test$Subset <- region
r_ssgsea_test <- r_ssgsea_test[, c("Pathway", "Subset", "Contrast", "Estimate",
"Pr(>|t|)", "FDR")]
lmm_ssgsea_results <- rbind(lmm_ssgsea_results, r_ssgsea_test)
}
lmm_ssgsea_results <- merge(lmm_ssgsea_results, pw_gene_df)
8.2.1 Table of Differental analysis of pathways
datatable(subset(lmm_ssgsea_results), rownames = FALSE,
extensions = 'Buttons', options = list (
pageLength = 10,
dom = 'Bftrip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
),
caption = "DE results for Pathways",filter='top') %>% formatRound(columns=c("Estimate","Pr(>|t|)","FDR"), digits=5)
8.3 Vulcanoplot of LMM_Pathways
# Categorize Results based on P-value & FDR for plotting
fc_threshold = 0.3
lmm_ssgsea_results$Color <- "NS or FC < 0.3"
lmm_ssgsea_results$Color[lmm_ssgsea_results$`Pr(>|t|)` < 0.05] <- "P < 0.05"
lmm_ssgsea_results$Color[lmm_ssgsea_results$FDR < 0.05] <- "FDR < 0.05"
lmm_ssgsea_results$Color[lmm_ssgsea_results$FDR < 0.001] <- "FDR < 0.001"
lmm_ssgsea_results$Color[abs(lmm_ssgsea_results$Estimate) < fc_threshold] <- "NS or FC < 0.3"
lmm_ssgsea_results$Color <- factor(lmm_ssgsea_results$Color,
levels = c("NS or FC < 0.3", "P < 0.05",
"FDR < 0.05", "FDR < 0.001"))
# pick top pw for either side of volcano to label
# order pw for convenience:
lmm_ssgsea_results$invert_P <- (-log10(lmm_ssgsea_results$`Pr(>|t|)`)) * sign(lmm_ssgsea_results$Estimate)
top_ssgsea_g <- c()
#loop here over tested conditions if applicable
for(c in unique(lmm_ssgsea_results$Subset) ) {
lmm_results_slice = lmm_ssgsea_results[lmm_ssgsea_results$Subset == c,]
top_ssgsea_g <- c(top_ssgsea_g,
lmm_ssgsea_results[ind, 'Pathway'][
order(lmm_ssgsea_results[ind, 'invert_P'], decreasing = TRUE)[1:15]],
lmm_ssgsea_results[ind, 'Pathway'][
order(lmm_ssgsea_results[ind, 'invert_P'], decreasing = FALSE)[1:15]])
}
top_ssgsea_g <- unique(top_ssgsea_g)
lmm_ssgsea_results <- lmm_ssgsea_results[, -1*ncol(lmm_ssgsea_results)] # remove invert_P from matrix
#lmm_ssgsea_results_slice <- lmm_ssgsea_results_slice[lmm_ssgsea_results_slice$FDR < 1,]
# Graph results
ggplot(lmm_ssgsea_results,
aes(x = Estimate, y = -log10(`Pr(>|t|)`),
color = Color, label = Pathway)) +
geom_vline(xintercept = c(0.2, -0.2), lty = "dashed") +
geom_hline(yintercept = -log10(0.05), lty = "dashed") +
geom_point() +
labs(x = paste(lmm_results_slice$Contrast, "log2(FC)"),
y = "Significance, -log10(P)",
color = "Significance") +
scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
`FDR < 0.05` = "lightblue",
`P < 0.05` = "orange2",
`NS or FC < 0.5` = "gray"),
guide = guide_legend(override.aes = list(size = 4))) +
scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
geom_text_repel(data = subset(lmm_ssgsea_results, Color == "FDR < 0.05" | Color == "FDR < 0.001"),
point.padding = 0.15, color = "black",size=3,
min.segment.length = .1, box.padding = .2, lwd = 2,
max.overlaps = 50) +
theme_bw(base_size = 16) +
theme(legend.position = "bottom") +
facet_wrap(~Subset, scales = "free_y")

# +facet_wrap(~Subset, scales = "free_y"))
8.4 heatmap of pathways
active_pw<-filter(lmm_ssgsea_results, `Pr(>|t|)` < 0.001)$Pathway
active_pw<-filter(lmm_ssgsea_results, FDR < 0.001 )$Pathway
#active_pw<-filter(lmm_ssgsea_results, Color == "FDR < 0.001")$Pathway
active_pw<-top_ssgsea_g
if (length(active_pw)>1) {
print("go")
pw_matrix<-assayDataElement(geneSetObj, elt = "exprs")
pheatmap(pw_matrix[active_pw,],
scale = "row",
show_rownames = TRUE, show_colnames = TRUE,
fontsize_row = 10,
border_color = NA,
clustering_method = "average",
#clustering_distance_rows = "correlation",
clustering_distance_cols = "euclidean",
cutree_cols = 2, cutree_rows = 2,
breaks = seq(-3, 3, 0.05),
#color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
main = "Heatmap of selected Pathways",
annotation_colors = color_list,
annotation_col = pData(target_Data)[, ann_names])
}else{
print("No significant results to display")
}
## [1] "go"

8.5 fgsea pathway analysis
Another option for pathway analysis is the R software package called
Fast Gene Set Enrichment Analysis. It is a pre-ranked method that uses
the LMM results combined with the same pathway list from msigdbr as
GSVA.
fgsea_results_all <- c()
for (contrast in unique(lmm_results$Contrast)) {
ranks <- lmm_results$Estimate[lmm_results$Contrast == contrast]
names(ranks) <- lmm_results$Gene[lmm_results$Contrast == contrast]
fgsea_results <- fgsea(msigdbr_list,
ranks,
eps = 0.0,
minSize=15,
maxSize = 500)
fgsea_results$Contrast <- contrast
fgsea_results_all <- rbind(fgsea_results_all, fgsea_results)
}
fgsea_reactome <- fgsea_results_all[grep("REACTOME", pathway),]
fgsea_reactome$pathway <- gsub("REACTOME_", "", fgsea_reactome$pathway)
fgsea_reactome$pathway <- gsub("_", " ", fgsea_reactome$pathway)
# Categorize Results based on P-value & FDR for plotting
fc_threshold = 0.3
fgsea_results_all$Color <- "NS or FC < 0.3"
fgsea_results_all$Color[fgsea_results_all$pval < 0.05] <- "P < 0.05"
fgsea_results_all$Color[fgsea_results_all$padj < 0.05] <- "FDR < 0.05"
fgsea_results_all$Color[fgsea_results_all$padj < 0.001] <- "FDR < 0.001"
fgsea_results_all$Color[abs(fgsea_results_all$ES) < fc_threshold] <- "NS or FC < 0.3"
fgsea_results_all$Color <- factor(fgsea_results_all$Color,
levels = c("NS or FC < 0.3", "P < 0.05",
"FDR < 0.05", "FDR < 0.001"))
plt <- htmltools::tagList()
counter = 1
for(c in unique(fgsea_results_all$Contrast) ) {
fgsea_results_slice = fgsea_results_all[fgsea_results_all$Contrast == c,]
# # pick top genes for either side of volcano to label
# # order genes for convenience:
fgsea_results_slice$invert_P <- (-log10(fgsea_results_slice$pval)) * sign(fgsea_results_slice$ES)
# #loop here over tested conditions if applicable
#top_fgsea_g <- c()
top_fgsea_g <- c(fgsea_results_slice[, 'pathway'][
order(fgsea_results_slice[, 'invert_P'], decreasing = TRUE)[1:15]],
fgsea_results_slice[, 'pathway'][
order(fgsea_results_slice[, 'invert_P'], decreasing = FALSE)[1:15]])
# for (celltype in unique(lmm_results$Subset)) {
# top_fgsea_g <- c(top_fgsea_g,
# fgsea_results_slice[fgsea_results_slice$Subset==celltype, 'pathway'][
# order(fgsea_results_slice[, 'invert_P'], decreasing = TRUE)[1:20]],
# fgsea_results_slice[fgsea_results_slice$Subset==celltype, 'pathway'][
# order(fgsea_results_slice[, 'invert_P'], decreasing = FALSE)[1:20]])
# }
top_fgsea_g <- unique(top_fgsea_g)
#fgsea_results_slice <- fgsea_results_slice[, -1*ncol(fgsea_results_slice)] # remove invert_P from matrix
# Graph results
dynplot <- ggplot(fgsea_results_slice,
aes(x = NES, y = -log10(pval),
color = Color, label = pathway)) +
geom_vline(xintercept = c(fc_threshold,-fc_threshold), lty = "dashed") +
geom_hline(yintercept = -log10(0.05), lty = "dashed") +
geom_point() +
labs(x = paste(c, " FC"),
y = "Significance, -log10(P)",
color = "Significance") +
scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
`FDR < 0.05` = "lightblue",
`P < 0.05` = "orange2",
`NS or FC < 0.3` = "gray"),
guide = guide_legend(override.aes = list(size = 4))) +
scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
geom_text_repel(data = subset(fgsea_results_slice, Color == "P < 0.05" | Color == "FDR < 0.05" | Color == "FDR < 0.001"),
point.padding = 0.15, color = "black",size=5,
min.segment.length = .1, box.padding = .2, lwd = 2,
max.overlaps = 50) +
theme_bw(base_size = 16) +
theme(legend.position = "bottom") #+
#facet_wrap(~Subset, scales = "free_y")
plt[[counter]] <- as_widget(ggplotly(dynplot))
counter <- counter + 1
#datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
}
plt
8.6 Visualize KEGG pathway
Get pathway visualization from KEGG. clusterProfiler grabs the KEGG
pathway IDs and combined with pathview it can download a pathway image
from the KEGG database. The LMM results are given with the pathway id to
visualize up and down regulated genes.
get_wp_gmtfile <- function() {
wpurl <- 'https://wikipathways-data.wmcloud.org/current/gmt/'
x <- readLines(wpurl)
y <- x[grep('\\.gmt',x)]
sub(".*(wikipathways-.*\\.gmt).*", "\\1", y[grep('File', y)])
}
get_wp_data <- function(organism) {
organism <- sub(" ", "_", organism)
gmtfile <- get_wp_gmtfile()
wpurl <- 'https://wikipathways-data.wmcloud.org/current/gmt/'
url <- paste0(wpurl,
gmtfile[grep(organism, gmtfile)])
f <- tempfile(fileext = ".gmt")
dl <- mydownload(url, destfile = f)
if (is.null(f)) {
message("fail to download wikiPathways data...")
return(NULL)
}
read.gmt.wp(f)
}
# enrichKEGG grab online pathways
gene <- target_Data@featureData@data[["GeneID"]]
kk <- enrichKEGG(gene = gene,
organism = 'hsa',
pvalueCutoff = 0.1)
## Reading KEGG annotation online: "https://rest.kegg.jp/link/hsa/pathway"...
## Reading KEGG annotation online: "https://rest.kegg.jp/list/pathway/hsa"...
enrichKEGG_results <- kk@result
# Match column values for link
# fsgea run
fgsea_results$Description <- gsub("KEGG_", "", fgsea_results$pathway)
fgsea_results$Description <- gsub("_", " ", fgsea_results$Description)
# ssgea run
# lmm_ssgsea_results_d$Description <- gsub("KEGG_", "", lmm_ssgsea_results_d$Pathway)
# lmm_ssgsea_results_d$Description <- gsub("_", " ", lmm_ssgsea_results_d$Description)
enrichKEGG_results$Description <- toupper(enrichKEGG_results$Description)
# Create df for matching KEGG results
KEGGIDs <- fgsea_results#[fgsea_results$Contrast == "Pro_Tub - Dis_Tub"]
KEGGIDs <- KEGGIDs %>% inner_join(enrichKEGG_results, by = 'Description') %>% select(Description, ID, geneID, leadingEdge, padj)
# Create geneList with DE results for visualization pathway genes
genelist <- lmm_results$Estimate
names(genelist) <- target_Data@featureData@data[["GeneID"]]
# Choose manual or highest significant pathway
pathway_name <- "INSULIN SIGNALING PATHWAY" # manual pathway query
pathway_name <- toupper(pathway_name)
if (pathway_name == "") {
pathway_name <- KEGGIDs[order(KEGGIDs$padj, decreasing = FALSE), ][1]$Description
}
pathwayid <- KEGGIDs$ID[KEGGIDs$Description == pathway_name]
# Grab pathway image with gene info, save and plot
viewPath <- pathview(gene.data = genelist,
pathway.id = pathwayid,
species = "hsa",
out.suffix = "genesinfo", kegg.native = T, same.layer = F)
## Info: Downloading xml files for hsa04910, 1/1 pathways..
## Info: Downloading png files for hsa04910, 1/1 pathways..
## 'select()' returned 1:1 mapping between keys and columns
## Info: Working in directory C:/Users/pkloosterman/Documents/GitHub/GeoMX-analysis/Data/DKD_Kidney
## Info: Writing image file hsa04910.genesinfo.png
img <- readPNG(paste(pathwayid, ".genesinfo.png", sep = ""))
grid::grid.raster(img)

Barplot pathways
Clear vizualization of the top 15 positive and negative expressed
Reactome pathways. Pathways with a adjusted p-value of 0.05 or higher
will be presented as red and below 0.05 as green.
top_all <- c()
for (contrast in unique(fgsea_reactome$Contrast)) {
# active_group1 <- contrast_list[contrast][[1]][[1]]
# active_group2 <- contrast_list[contrast][[1]][[2]]
# ind <- pData(target_Data)[pData(target_Data)$ANN2 == active_group1 | pData(target_Data)$ANN2 == active_group2]
top <- fgsea_reactome[fgsea_reactome$Contrast == contrast]
toppos <- top[order(top$NES, decreasing = TRUE)][0:15]
topneg <- top[order(top$NES, decreasing = FALSE)][0:15]
top <- rbind(toppos, topneg)
top$Color[top$padj < 0.05] <- "padj < 0.05"
top$Color[top$padj == 0.05 | top$padj > 0.05] <- "padj > 0.05"
top_all <- rbind(top_all, top)
barplot <- ggplot(top,aes(x=reorder(pathway, NES),y=NES,fill=Color)) +
scale_x_discrete(labels = function(x) str_wrap(x, width = 100)) +
geom_col(position="dodge") + scale_fill_manual(values = c("#2ECC71", "#E74C3C")) +
ggtitle(paste("Top 15 + and - NES reactome pathways from", contrast)) +
xlab("pathway") + ylab("Normalized Enrichment Score") +
coord_flip() +
theme(text = element_text(size = 20)) +
theme(panel.background = element_blank())
#facet_wrap(~Contrast, scales = "free_y")
print(barplot)
}

9 Spatial Deconvolution
9.1 Calculate backgrounds
#gc()
bg = derive_GeoMx_background(
norm = assayDataElement(target_Data , elt = "q_norm"),
probepool = fData(target_Data)$Module,
negnames = c("NegProbe-CTP01","NegProbe-Kilo","Negative Probe", "NegProbe-WTX" ))
#negnames = "NegProbe-WTX")
9.2 Load cell profile
A “cell profile matrix” is a pre-defined matrix that specifies the
expected expression profiles of each cell type in the experiment. The
SpatialDecon library comes with one such matrix pre-loaded, the
“SafeTME” matrix, designed for estimation of immune and stroma cells in
the tumor microenvironment. (This matrix was designed to avoid genes
commonly expressed by cancer cells; see the SpatialDecon manuscript for
details.). Otherwise, load specific profiles from https://github.com/Nanostring-Biostats/CellProfileLibrary/tree/NewProfileMatrices
#safeTME
data("safeTME")
data("safeTME.matches")
current_cell_profile<-safeTME
#see: https://github.com/Nanostring-Biostats/CellProfileLibrary/tree/NewProfileMatrices
current_cell_profile <- download_profile_matrix(species = "Human",
age_group = "Adult",
matrixname = "Kidney_HCA")
heatmap(sweep(current_cell_profile, 1, apply(current_cell_profile, 1, max), "/"),
labRow = NA, margins = c(10, 5), cexCol = 0.7)

9.3 Run spatial deconvolution
# vector identifying pure tumor segments:
#target_Data$istumor = target_Data$ANN3 == "CORE" & target_Data$ANN1 == "PanCK+"
res = runspatialdecon(object = target_Data,
norm_elt = "q_norm",
raw_elt = "exprs",
#is_pure_tumor = target_Data$istumor,
cell_counts = target_Data$nuclei,
X = current_cell_profile,
#cellmerges = safeTME.matches, # safeTME.matches object, used by default
#n_tumor_clusters = 5, # how many distinct tumor profiles to append to safeTME
align_genes = TRUE)
9.3.1 Spatial deconvolution
heatmaps
Abundance
# NOTE: check clustering.. why different?
#set display thresholds
thresh <- signif(quantile(res$beta, 0.97), 2)
# plot stored to keep clustering for later
p1<-pheatmap(pmin(t(res$beta),thresh),
#scale = "row",
cutree_cols = 3,
cutree_rows = 2,
fontsize_row = 12,
show_rownames = TRUE, show_colnames = TRUE,
angle_col = "90",
border_color = NA,
#clustering_method = "average",
#clustering_distance_rows = "correlation",
#clustering_distance_cols = "correlation",
legend_breaks = c(round(seq(0, thresh, length.out = 5))[-5], thresh),
legend_labels = c(round(seq(0, thresh, length.out = 5))[-5], paste0("Abundance scores,\ntruncated above at ", thresh)),
#breaks = seq(0, 5, 1),
color = colorRampPalette(c("white","darkblue"))(100),
annotation_colors = color_list,
annotation_col = pData(target_Data)[, ann_names]
)

#p1
Proportional
# proportions:
props <- replace(res$prop_of_nontumor, is.na(res$prop_of_nontumor), 0)
p2<-pheatmap(t(props),
#scale = "row",
cutree_cols = 3,
cutree_rows = 2,
fontsize_row = 12,
show_rownames = TRUE, show_colnames = TRUE,
angle_col = "90",
border_color = NA,
#clustering_method = "average",
#clustering_distance_rows = "correlation",
#clustering_distance_cols = "correlation",
legend_breaks = round(seq(0, max(props) * 0.99, length.out = 5), 2),
legend_labels = c(round(seq(0, max(props), length.out = 5), 2)[-5], "Proportion of all\nfitted populations"),
color = colorRampPalette(c("white","darkblue"))(100),
annotation_colors = color_list,
annotation_col = pData(target_Data)[, ann_names])

#p2
Scaled
# scaled abundances:
epsilon <- min(res$beta[res$beta > 0])
mat <- sweep(res$beta, 1, pmax(apply(res$beta, 1, max), epsilon), "/")
pheatmap(t(mat),
#scale = "row",
cutree_cols = 3,
cutree_rows = 3,
fontsize_row = 12,
show_rownames = TRUE, show_colnames = TRUE,
angle_col = "90",
border_color = NA,
#clustering_method = "average",
#clustering_distance_rows = "correlation",
#clustering_distance_cols = "correlation",
legend_breaks = c(round(seq(0, 1, length.out = 5), 2)[-5], 1),
legend_labels = c(round(seq(0, 1, length.out = 5), 2)[-5], "Scaled abundance\n(ratio to max)"),
color = colorRampPalette(c("white","darkblue"))(100),
annotation_colors = color_list,
annotation_col = pData(target_Data)[, ann_names])

9.4 Barplots
abundance
# define variables to show in heatmaps:
pData(target_Data)$region <-
factor(pData(target_Data)$ANN3, unique(target_Data$ANN3)) # Must be manual?
pData(target_Data)$class <-
factor(pData(target_Data)$ANN1, unique(target_Data$ANN1)) # Must be manual?
variables_to_plot <- c("slide_name", "ANN1", "ANN3") # Must be manual?
#define colors
# only for safeTME colors
#col <- cellcols
#custom celmatrix
#get large number of colors
qual_col_pals = brewer.pal.info[brewer.pal.info$category == 'qual',]
col_vector = unique(unlist(mapply(brewer.pal, qual_col_pals$maxcolors, rownames(qual_col_pals))))
celltypes<-sample(col_vector,length(colnames(res$beta)))
names(celltypes)<-colnames(res$beta)
col<-celltypes
#tempfix for abbreviated and now mismatching annotations
#can just use ann dataframe?
#tmpann<-cbind(ANN1,ANN2,SN)
tmpann <- pData(target_Data)[ann_names]
layout(matrix(c(1, 2, 3, 3), nrow = 2),
widths = c(10, 3, 10, 3),
heights = c(1, 8, 10),
)
par(mar = c(0, 8.2, 0, 0.2))
plot(p1$tree_col, labels = F, main = "", ylab = "", yaxt = "n")
par(mar = c(15, 8, 0, 0))
# data to plot:
mat <- t(res$beta)[, p1$tree_col$order]
# infer scale of negative y-axis for annotation colorbars
ymin <- -max(colSums(mat)) * 0.15
if (!is.finite(ymin)) {
ymin <- 0
}
# draw barplot:
bp <- barplot(mat,
cex.lab = 1.5,
col = col, border = NA,
cex.names = 1.1,
las = 2, main = "", ylab = "Abundance scores",
ylim = c(ymin, max(colSums(mat)))
)
# add color bars for annotations
for (name in rev(variables_to_plot)) {
yrange <- seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 1)[match(name, variables_to_plot) + c(0, 1)]
xwidth <- (bp[2] - bp[1]) / 2
for (i in 1:ncol(mat)) {
rect(bp[i] - xwidth, yrange[2], bp[i] + xwidth, yrange[1],
# border = NA, col = ann_colors[[name]][segmentAnnotations[match(colnames(mat)[i], segmentAnnotations$segmentID), name]]
#border = NA, col = ann_colors[[name]][ann[p1$tree_col$order[i], name]]
border = NA, col = color_list[[name]][tmpann[colnames(mat)[i], name]]
)
}
}
axis(2,
at = seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 2)[-c(1, length(variables_to_plot) + 2)],
las = 2, labels = variables_to_plot, lty = 0, cex.axis = 1.2
)
#draw a legend:
par(mar = c(0.1, 0.1, 0.1, 0.1))
frame()
legendcols <- legendnames <- c()
#for (name in rev(names(ann_colors))) {
for (name in c("slide_name", "ANN1", "ANN3")) {
legendcols <- c(legendcols, NA, color_list[[name]], NA)
legendnames <- c(legendnames, name, names(color_list[[name]]), NA)
}
legend("center",
pch = 15,
cex = 1.5,
col = c(legendcols, rep(NA, 1), rev(col)),
legend = c(legendnames, "Cell type", rev(names(col))),
)

proportional
# define variables to show in heatmaps:
variables_to_plot <- c("slide_name", "ANN1", "ANN3")
layout(matrix(c(1, 2, 3, 3), nrow = 2),
widths = c(10, 3, 10, 3),
heights = c(1, 8, 10),
)
par(mar = c(0, 8.2, 0, 0.2))
plot(p2$tree_col, labels = F, main = "", ylab = "", yaxt = "n")
par(mar = c(15, 8, 0, 0))
# data to plot:
mat <- t(res$prop_of_nontumor)[, p2$tree_col$order]
mat <- replace(mat, is.na(mat), 0)
# infer scale of negative y-axis for annotation colorbars
ymin <- -0.15
# draw barplot:
bp <- barplot(mat,
cex.lab = 1.5,
col = col, border = NA,
cex.names = 1.1,
las = 2, main = "", ylab = "Proportion of fitted cells",
ylim = c(ymin, max(colSums(mat)))
)
# add color bars for annotations
for (name in rev(variables_to_plot)) {
yrange <- seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 1)[match(name, variables_to_plot) + c(0, 1)]
xwidth <- (bp[2] - bp[1]) / 2
for (i in 1:ncol(mat)) {
rect(bp[i] - xwidth, yrange[2], bp[i] + xwidth, yrange[1],
# border = NA, col = ann_colors[[name]][segmentAnnotations[match(colnames(mat)[i], segmentAnnotations$segmentID), name]]
#border = NA, col = ann_colors[[name]][ann[p2$tree_col$order[i], name]]
border = NA, col = color_list[[name]][tmpann[colnames(mat)[i], name]]
)
}
}
axis(2,
at = seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 2)[-c(1, length(variables_to_plot) + 2)],
las = 2, labels = variables_to_plot, lty = 0, cex.axis = 1.2
)
#draw a legend:
par(mar = c(0.1, 0.1, 0.1, 0.1))
frame()
legendcols <- legendnames <- c()
#for (name in rev(names(ann_colors))) {
for (name in c("slide_name", "ANN1", "ANN3")) {
legendcols <- c(legendcols, NA, color_list[[name]], NA)
legendnames <- c(legendnames, name, names(color_list[[name]]), NA)
}
legend("center",
pch = 15,
cex = 1.4,
col = c(legendcols, rep(NA, 1), rev(col)),
legend = c(legendnames, "Cell type", rev(names(col))),
)

Boxplots
Boxplot per cell from the spatial deconvolution. Choose your
annotation in which you want to compare (ANN). The annotation will be
divided in conditions and each cell with have a boxplot which include
the abundance score of each condition. A statistical test has been added
inside the boxplot to check for significance. Feel free to chance the
test to fit the data.
significance code p-value *** [0, 0.001] ** (0.001, 0.01] * (0.01,
0.05] . (0.05, 0.1] (0.1, 1]
ANN <- "ANN1" # ANN column with conditions
if (length(unique(target_Data[[ANN]])) > 2) {
test <- "kruskal.test"
} else {
test <- "wilcox.test"
}
res_filter <- res[!res@phenoData@data[["beta"]]==0]
res_filter <- as.data.frame(res_filter$beta)
res_filter[res_filter == 0] <- NA
boxplot_prep <- res_filter[ , colSums(is.na(res_filter)) < nrow(res_filter)]
boxplot_prep$condition <- rownames(boxplot_prep)
for (value in unique(target_Data[[ANN]])) {
con <- rownames(pData(target_Data)[pData(target_Data)[[ANN]] == value,])
boxplot_prep$condition[boxplot_prep$condition %in% con] <- value
}
boxplot <- melt(boxplot_prep)
## Using condition as id variables
# function for number of observations
give.n <- function(x){
return(data.frame(y = -1, label = paste0("n = ",length(x))))
#return(c(y = median(x)*1.05, label = length(x))
# experiment with the multiplier to find the perfect position
}
ggplot(boxplot, aes(factor(condition), value, fill = condition)) +
geom_boxplot() +
geom_jitter(position=position_jitter(w=0.1,h=0.1)) +
facet_wrap(~variable, scale="free", ncol = 5) +
theme(text = element_text(size = 20)) +
rotate_x_text(angle = 45) +
labs(
title = "Spatial Deconvolution",
x = "Conditions",
y = "Abuncance score") +
stat_compare_means(method = test, label.y = -3, size=5) +
stat_compare_means(label = "p.signif", method = "t.test",
ref.group = ".all.", hide.ns = TRUE, size=5) +
stat_summary(fun.data = give.n, geom = "text", size = 5)

#stat_compare_means(size=5)
11 CNV
Copy Number Variation analysis with the R software package inferCNV
(https://github.com/broadinstitute/inferCNV/wiki).
InferCNV is used to explore tumor single cell RNA-Seq data to
identify evidence for somatic large-scale chromosomal copy number
alterations, such as gains or deletions of entire chromosomes or large
segments of chromosomes. This is done by exploring expression intensity
of genes across positions of tumor genome in comparison to a set of
reference ‘normal’ cells. A heatmap is generated illustrating the
relative expression intensities across each chromosome, and it often
becomes readily apparent as to which regions of the tumor genome are
over-abundant or less-abundant as compared to that of normal cells.
Per patient a cnv analysis with a provided reference group. The gene
order is made from the projects .pkc file.
Select the annotation where the CNV analysis should look at
specifically as group and specify a subgroup as group_filter if the
group of interest is a subgroup inside the annotation. For example if
the CNV analysis needs to be only on tumor regions the group would be
ANNX(ANN column with the info about the tumor regions) and group_filter
PanCK+. The annotation that references the patients as patients. If
there are no patients leave the parameter empty as ““. The annotation
that should be included in the results including a reference set as
cnv_target. This will create patient specific files with all the
information inferCNV needs.
Select the reference set in reference. This can be more than one.
Every patient without its own reference will not be analysed.
#########PARAMETERS########
reference <- c("normal")
###########################
failed <- c()
cnv_list <- list()
for (patient in patient_list) {
# Name output folder
out_dir <- paste0(patient, "_CNV")
if (str_detect(paste(readLines(paste0(patient, ".Annotations.tsv")), collapse = ''), reference) == FALSE) {
failed <- c(failed, patient)
next
}
# Create the infercnv object
infercnv_obj = CreateInfercnvObject(raw_counts_matrix= paste0(patient, ".Counts.tsv"),
annotations_file= paste0(patient, ".Annotations.tsv"),
delim="\t",
gene_order_file= "gene_order_Hs_WTA_v1_pkc.txt",
#gene_order_file= "gencode_v19_gene_pos.txt",
ref_group_names= reference, # input the normal/reference group names
chr_exclude = c("chrM"))#c("chrM")) # Default excludes chrX, chrY and chrM. By only picking chrM you include the X and Y chromosomes.
# perform infercnv operations to reveal cnv signal. For all options: https://rdrr.io/github/broadinstitute/infercnv/man/run.html
#cnv_list[[patient]]
infercnv_obj <- infercnv::run(infercnv_obj,
cutoff=0.1, # use 1 for smart-seq, 0.1 for 10x-genomics
out_dir= out_dir, # dir is auto-created for storing outputs
cluster_by_groups=FALSE, # cluster
denoise=TRUE,
HMM=FALSE,
tumor_subcluster_partition_method = c("qnorm"),
analysis_mode = "subclusters",
no_plot=TRUE
#,debug=TRUE
)
plot_cnv(infercnv_obj,
out_dir = paste0(patient, "_CNV"),
title = "inferCNV",
obs_title = "Observations (Cells)",
ref_title = "References (Cells)",
cluster_by_groups = FALSE,
cluster_references = FALSE,
plot_chr_scale = FALSE,
#chr_lengths = NULL,
k_obs_groups = 1,
contig_cex = 1.5,
#x.center = mean(infercnv_obj@expr.data),
x.range = "auto",
#hclust_method = "ward.D",
output_filename = "infercnv",
output_format = "png",
png_res = 300
)
}
for (patient in patient_list) {
print(paste(group_filter, patient))
if (patient %in% failed) {
print(" ^ Patient contained no reference group")
next
}
img <- readPNG(paste(paste0(patient, "_CNV"), "/infercnv.png", sep = ""))
grid::grid.newpage()
grid::grid.raster(img)
}
## [1] " dummy"

11.1 Dendrogram
Closer look at the dendrogram from the CNV analysis per patient where
the nodes as written numbers. Use the numbers to select subgroups for
further analysis in ‘t-test on two subgroups’.
#out_dir <- "T1_NANO_012_CNV"
trees <- list()
for (patient in patient_list) {
if (patient %in% failed) {
#print("Patient contained no reference group")
next
}
tree <- read.tree(paste(patient,"_CNV/infercnv.observations_dendrogram.txt", sep = ""))
obv <- read.csv(paste(patient,"_CNV/infercnv.observation_groupings.txt", sep = ""), sep="")
trees[[patient]] <- ggtree(
tree, ladderize=F) +
geom_treescale() +
geom_tiplab(color=obv$Annotation.Color, hjust=-.2) +
coord_cartesian(clip = 'off') +
theme_tree2(plot.margin=margin(6, 200, 6, 6)) +
geom_text2(aes(label=node), hjust=-.3, size = 3) +
ggplot2::labs(title = patient)
}
grid.arrange(grobs=trees,ncol=3)

t-test chr with groups
Compare the contrast within a chromosome. Select a chromosome,
contrast, and patient of interest to run a t-test on.
#########PARAMETERS########
chr <- "chrX" # Select chromosome of interest
contrast <- c("normal", "DKD") # Select contrast
patient <- "dummy"
###########################
positions <- as.data.frame(positions)
colnames(positions) <- c("gene", "chr", "begin", "end")
select_genes <- positions$gene[positions$chr == chr] # Grab chr specific genes
annotation <- as.data.frame(read.delim(paste0(patient, ".Annotations.tsv"), header=FALSE))
colnames(annotation) <- c("Sample_ID", "ANN")
select_samples <- annotation
filter_counts <- as.data.frame(target_Data@assayData[["log_q"]])
colnames(filter_counts) <- gsub('.dcc','', colnames(filter_counts))
filter_counts <- filter_counts[,select_samples$Sample_ID] # filter out samples that are not the interesting region
select_genes <- select_genes[select_genes %in% rownames(filter_counts)] # Only use genes that are actually in the data (pkc gene file has all of them)
filter_counts <- filter_counts[select_genes,] # filter out non chromosome specific genes
plots<-list()
tables<-list()
labels<-list()
test<-"ttest"
mtc<-"BH"
counter=1
log_q_filter <-as.data.frame(filter_counts)
comps_df<-data.frame(comp='',val='')
for (active_group1 in contrast) {
for (active_group2 in contrast) {
#supress reduncant compares
if(active_group1==active_group2) {next}
comp<-paste(sort(c(active_group1,active_group2)),collapse = "_")
#print(comp)
if(comp %in% comps_df$comp) {next}
temp_df<-data.frame(comp=comp ,val=1)
comps_df<-rbind(comps_df,temp_df)
labels[[counter]]<-paste(active_group1," vs ", active_group2)
group1<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group1]]
group2<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group2]]
#run t_tests
results<-as.data.frame ( apply(log_q_filter, 1, function(x) t.test(x[colnames(group1)],x[colnames(group2)])$p.value) )
colnames(results)<-"raw_p_value"
#multiple_testing_correction
adj_p_value<- p.adjust(results$raw_p_value,method=mtc)
results<-cbind(results,adj_p_value)
#calc_fdr
FDR<- p.adjust(results$raw_p_value,method="fdr")
results<-cbind(results,FDR)
#fold_changes
#as base data is already log transformed, means need to be subtracted to get FC in log space
fchanges<-as.data.frame( apply(log_q_filter, 1, function(x) (mean(x[colnames(group1)]) - mean(x[colnames(group2)]) ) ) )
colnames(fchanges)<-"FC"
results<-cbind(results,fchanges)
#add genenames
results$Gene<-rownames(results)
#set categories based on P-value & FDR for plotting
results$Color <- "NS or FC < 0.5"
results$Color[results$adj_p_value < 0.05] <- "P < 0.05"
results$Color[results$FDR < 0.05] <- "FDR < 0.05"
results$Color[results$FDR < 0.001] <- "FDR < 0.001"
results$Color[abs(results$FC) < 0.5] <- "NS or FC < 0.5"
results$Color <- factor(results$Color,
levels = c("NS or FC < 0.5", "P < 0.05", "FDR < 0.05", "FDR < 0.001"))
#vulcanoplot
# pick top genes for either side of volcano to label
# order genes for convenience:
results$invert_P <- (-log10(results$adj_p_value)) * sign(results$FC)
top_g <- c()
top_g <- c(top_g,
results[ind, 'Gene'][
order(results[ind, 'invert_P'], decreasing = TRUE)[1:15]],
results[ind, 'Gene'][order(results[ind, 'invert_P'], decreasing = FALSE)[1:15]])
top_g<- unique(top_g)
results <- results[, -1*ncol(results)] # remove invert_P from matrix
# Graph results
plots[[counter]]<- ggplot(results,
aes(x = FC, y = -log10(adj_p_value),
color = Color, label = Gene)) +
geom_vline(xintercept = c(0.5, -0.5), lty = "dashed") +
geom_hline(yintercept = -log10(0.05), lty = "dashed") +
geom_point() +
labs(x = paste("Enriched genes in", chr, "-", active_group2," <- log2(FC) -> Enriched in", active_group1),
y = "Significance, -log10(P)",
color = "Significance") +
scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
`FDR < 0.05` = "lightblue",
`P < 0.05` = "orange2",
`NS or FC < 0.5` = "gray"),
guide = guide_legend(override.aes = list(size = 4))) +
scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
geom_text_repel(data = subset(results, FDR<0.05 & (-0.5>FC| FC>0.5)),
point.padding = 0.15, color = "black", size=3.5,
min.segment.length = .1, box.padding = .2, lwd = 2,
max.overlaps = 50) +
theme_bw(base_size = 20) +
theme(legend.position = "bottom") +
#ggtitle(paste(slide,": ", test, mtc,"multitest corr"))
ggtitle(paste(patient, ": ", test, mtc,"multitest corr"))
#store tables for display later
tables[[counter]]<-results
counter = counter+1
#datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
}
}
grid.arrange(grobs=plots,ncol=2)

t-test on two subgroups
Select nodes from the dendrogram as subgroups, in this case nodes 18
and 15. Decide on the involved chromosomes and add them into the
chr_list. Specify the patient in patient.
#########PARAMETERS########
chr_list <- c("chr22") # Choose needed chromosomes of the target area. Think of it as the x-axis
patient <- "dummy"
node_interest <- 233 # Choose subgroup 1
node_interest2 <- 216 # Choose subgroup 2
###########################
tree <- read.tree(paste(patient,"_CNV/infercnv.observations_dendrogram.txt", sep = ""))
tree_info <- tree %>% as.treedata %>% as_tibble # transform tree into accessible data
samples_interest <- offspring(tree_info, node_interest) # Get labels attached to the group
samples_interest <- as.character(na.omit(samples_interest$label)) # formatting
paste("Subgroup 1: ", samples_interest)
## [1] "Subgroup 1: DSP-1001250007864-D-H08"
## [2] "Subgroup 1: DSP-1001250007864-D-H10"
## [3] "Subgroup 1: DSP-1001250007864-D-H06"
## [4] "Subgroup 1: DSP-1001250007864-D-H02"
## [5] "Subgroup 1: DSP-1001250007864-D-H04"
## [6] "Subgroup 1: DSP-1001250007864-D-H11"
## [7] "Subgroup 1: DSP-1001250007864-D-H12"
## [8] "Subgroup 1: DSP-1001250007864-D-H05"
## [9] "Subgroup 1: DSP-1001250007864-D-H07"
## [10] "Subgroup 1: DSP-1001250007864-D-H09"
## [11] "Subgroup 1: DSP-1001250007864-D-H01"
## [12] "Subgroup 1: DSP-1001250007864-D-H03"
## [13] "Subgroup 1: DSP-1001250007851-H-A09"
## [14] "Subgroup 1: DSP-1001250007851-H-C04"
## [15] "Subgroup 1: DSP-1001250007851-H-D06"
## [16] "Subgroup 1: DSP-1001250007851-H-C07"
## [17] "Subgroup 1: DSP-1001250007851-H-D12"
## [18] "Subgroup 1: DSP-1001250007851-H-A12"
## [19] "Subgroup 1: DSP-1001250007851-H-A03"
## [20] "Subgroup 1: DSP-1001250007851-H-D09"
## [21] "Subgroup 1: DSP-1001250007851-H-A04"
## [22] "Subgroup 1: DSP-1001250007851-H-D10"
## [23] "Subgroup 1: DSP-1001250007851-H-A10"
## [24] "Subgroup 1: DSP-1001250007851-H-C01"
## [25] "Subgroup 1: DSP-1001250007851-H-C03"
## [26] "Subgroup 1: DSP-1001250007851-H-A05"
## [27] "Subgroup 1: DSP-1001250007851-H-C02"
## [28] "Subgroup 1: DSP-1001250007851-H-B12"
## [29] "Subgroup 1: DSP-1001250007851-H-B06"
## [30] "Subgroup 1: DSP-1001250007851-H-B11"
## [31] "Subgroup 1: DSP-1001250007851-H-B04"
## [32] "Subgroup 1: DSP-1001250007851-H-B05"
## [33] "Subgroup 1: DSP-1001250007851-H-D11"
## [34] "Subgroup 1: DSP-1001250007851-H-D05"
## [35] "Subgroup 1: DSP-1001250007851-H-B07"
## [36] "Subgroup 1: DSP-1001250007851-H-C08"
## [37] "Subgroup 1: DSP-1001250007851-H-D08"
## [38] "Subgroup 1: DSP-1001250007851-H-B10"
## [39] "Subgroup 1: DSP-1001250007851-H-A02"
## [40] "Subgroup 1: DSP-1001250007851-H-A08"
## [41] "Subgroup 1: DSP-1001250007851-H-A06"
## [42] "Subgroup 1: DSP-1001250007851-H-A11"
## [43] "Subgroup 1: DSP-1001250007851-H-D04"
## [44] "Subgroup 1: DSP-1001250007851-H-D07"
## [45] "Subgroup 1: DSP-1001250007851-H-B01"
## [46] "Subgroup 1: DSP-1001250007851-H-B08"
## [47] "Subgroup 1: DSP-1001250007851-H-A07"
## [48] "Subgroup 1: DSP-1001250007851-H-B02"
## [49] "Subgroup 1: DSP-1001250007851-H-B03"
## [50] "Subgroup 1: DSP-1001250007851-H-C06"
## [51] "Subgroup 1: DSP-1001250007851-H-D01"
## [52] "Subgroup 1: DSP-1001250007851-H-C12"
## [53] "Subgroup 1: DSP-1001250007851-H-C09"
## [54] "Subgroup 1: DSP-1001250007851-H-C05"
## [55] "Subgroup 1: DSP-1001250007851-H-C11"
## [56] "Subgroup 1: DSP-1001250007851-H-C10"
## [57] "Subgroup 1: DSP-1001250007851-H-B09"
## [58] "Subgroup 1: DSP-1001250007851-H-D03"
## [59] "Subgroup 1: DSP-1001250007851-H-D02"
## [60] "Subgroup 1: DSP-1001250007868-B-A02"
samples_interest2 <- offspring(tree_info, node_interest2)
samples_interest2 <- as.character(na.omit(samples_interest2$label))
paste("Subgroup 2: ", samples_interest2)
## [1] "Subgroup 2: DSP-1001250007851-H-F10"
## [2] "Subgroup 2: DSP-1001250007851-H-F04"
## [3] "Subgroup 2: DSP-1001250007851-H-F11"
## [4] "Subgroup 2: DSP-1001250007851-H-F01"
## [5] "Subgroup 2: DSP-1001250007851-H-F05"
## [6] "Subgroup 2: DSP-1001250007851-H-E08"
## [7] "Subgroup 2: DSP-1001250007851-H-F03"
## [8] "Subgroup 2: DSP-1001250007851-H-F02"
## [9] "Subgroup 2: DSP-1001250007851-H-F09"
## [10] "Subgroup 2: DSP-1001250007851-H-F06"
## [11] "Subgroup 2: DSP-1001250007851-H-F07"
## [12] "Subgroup 2: DSP-1001250007868-B-A05"
## [13] "Subgroup 2: DSP-1001250007868-B-A03"
## [14] "Subgroup 2: DSP-1001250007868-B-B01"
## [15] "Subgroup 2: DSP-1001250007851-H-F12"
## [16] "Subgroup 2: DSP-1001250007868-B-A04"
## [17] "Subgroup 2: DSP-1002510007866-C-G02"
## [18] "Subgroup 2: DSP-1002510007866-C-H12"
positions <- as.data.frame(positions)
colnames(positions) <- c("gene", "chr", "begin", "end") # Formatting
select_genes <- positions$gene[positions$chr %in% chr_list] # | positions$chr == chr2] # Grab chromosome specific genes
annotation <- as.data.frame(read.delim(paste0(patient, ".Annotations.tsv"), header=FALSE))
colnames(annotation) <- c("Sample_ID", "ANN")
select_samples <- annotation[annotation$Sample_ID %in% samples_interest | annotation$Sample_ID %in% samples_interest2,] # Grab subgroup specific Sample IDs
filter_counts <- target_Data@assayData[["log_q"]]
colnames(filter_counts) <- gsub('.dcc','', colnames(filter_counts))
filter_counts <- filter_counts[,select_samples$Sample_ID] # Filter Sample ID's
select_genes <- select_genes[select_genes %in% rownames(filter_counts)] # Only use genes that are actually in the data (pkc gene file has all of them)
filter_counts <- filter_counts[select_genes,] # Filter genes
plots<-list()
tables<-list()
labels<-list()
test<-"ttest"
mtc<-"BH"
counter=1
log_q_filter <-as.data.frame(filter_counts)
comps_df<-data.frame(comp='',val='')
active_group1 <- samples_interest #subgroup 1
active_group2 <- samples_interest2 #subgroup 2
# for (active_group1 in c("sub1")) {
# for (active_group2 in c("sub2")) {
#supress reduncant compares
#if(active_group1==active_group2) {next}
#comp<-paste(sort(c(active_group1,active_group2)),collapse = "_")
#print(comp)
#if(comp %in% comps_df$comp) {next}
temp_df<-data.frame(comp=comp ,val=1)
comps_df<-rbind(comps_df,temp_df)
# labels[[counter]]<-paste(active_group1," vs ", active_group2)
# group1<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group1]]
# group2<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group2]]
labels[[counter]]<-paste(active_group1," vs ", active_group2)
group1<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$Sample_ID %in% active_group1]]
group2<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$Sample_ID %in% active_group2]]
#run t_tests
results<-as.data.frame ( apply(log_q_filter, 1, function(x) t.test(x[colnames(group1)],x[colnames(group2)])$p.value) )
colnames(results)<-"raw_p_value"
#multiple_testing_correction
adj_p_value<- p.adjust(results$raw_p_value,method=mtc)
results<-cbind(results,adj_p_value)
#calc_fdr
FDR<- p.adjust(results$raw_p_value,method="fdr")
results<-cbind(results,FDR)
#fold_changes
#as base data is already log transformed, means need to be subtracted to get FC in log space
fchanges<-as.data.frame( apply(log_q_filter, 1, function(x) (mean(x[colnames(group1)]) - mean(x[colnames(group2)]) ) ) )
colnames(fchanges)<-"FC"
results<-cbind(results,fchanges)
#add genenames
results$Gene<-rownames(results)
#set categories based on P-value & FDR for plotting
results$Color <- "NS or FC < 0.5"
results$Color[results$adj_p_value < 0.05] <- "P < 0.05"
results$Color[results$FDR < 0.05] <- "FDR < 0.05"
results$Color[results$FDR < 0.001] <- "FDR < 0.001"
results$Color[abs(results$FC) < 0.5] <- "NS or FC < 0.5"
results$Color <- factor(results$Color,
levels = c("NS or FC < 0.5", "P < 0.05", "FDR < 0.05", "FDR < 0.001"))
#vulcanoplot
# pick top genes for either side of volcano to label
# order genes for convenience:
results$invert_P <- (-log10(results$adj_p_value)) * sign(results$FC)
top_g <- c()
top_g <- c(top_g,
results[ind, 'Gene'][
order(results[ind, 'invert_P'], decreasing = TRUE)[1:15]],
results[ind, 'Gene'][order(results[ind, 'invert_P'], decreasing = FALSE)[1:15]])
top_g<- unique(top_g)
results <- results[, -1*ncol(results)] # remove invert_P from matrix
# Graph results
#plots[[counter]]<- ggplot(results,
p <- ggplot(results,
aes(x = FC, y = -log10(adj_p_value),
color = Color, label = Gene)) +
geom_vline(xintercept = c(0.5, -0.5), lty = "dashed") +
geom_hline(yintercept = -log10(0.05), lty = "dashed") +
geom_point() +
labs(x = paste("Enriched genes from", chr_list, "in", "subgroup 2"," <- log2(FC) -> Enriched in", "subgroup 1"),
y = "Significance, -log10(P)",
color = "Significance") +
scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
`FDR < 0.05` = "lightblue",
`P < 0.05` = "orange2",
`NS or FC < 0.5` = "gray"),
guide = guide_legend(override.aes = list(size = 4))) +
scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
geom_text_repel(data = subset(results, FDR<0.05 & (-0.5>FC| FC>0.5)),
point.padding = 0.15, color = "black", size=3.5,
min.segment.length = .1, box.padding = .2, lwd = 2,
max.overlaps = 50) +
theme_bw(base_size = 20) +
theme(legend.position = "bottom") +
#ggtitle(paste(slide,": ", test, mtc,"multitest corr"))
ggtitle(paste(patient, ": ", test, mtc,"multitest corr"))
#store tables for display later
tables[[counter]]<-results
counter = counter+1
#datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
# }
# }
#ggplotly(p)
p

#grid.arrange(grobs=plots,ncol=2)
LS0tDQp0aXRsZTogIk5hbm9zdHJpbmcgR2VvTXggYW5hbHlzaXMiDQphdXRob3I6ICJJZXMgTmlqbWFuICYgUGltIEtsb29zdGVybWFuIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgIHRvY19kZXB0aDogMw0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UpDQpgYGANCg0KIVtdKGh0dHA6Ly91c2VxLm5sL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDIyLzEyL1VTRVEtbG9nby1zdWJ0aXRsZS5wbmcpDQoNCiMgS2xhbnQ6IFVzZXENCg0KIyBQcm9qZWN0OiBTcGF0aWFsIE9yZ2FuIEF0bGFzDQoNCiMgRGF0YXNldDogS2lkbmV5DQoNCioqZGF0ZTogYHIgZm9ybWF0KFN5cy50aW1lKCksICclSCAlTSAlYSAlZCAlQiwgJVknKWAqKg0KDQohW10oaHR0cDovL3VzZXEubmwvd3AtY29udGVudC91cGxvYWRzLzIwMjIvMTIvZGVjb3JhdGlvbi1zdHJva2UtZmxhdC5wbmcpDQoNCioqbG9hZGluZyBkZXBlbmRlbmNpZXMqKiBQbGVhc2UgbWFrZSBzdXJlIHRoZSBmb2xsb3dpbmcgcGFja2FnZXMgYXJlDQppbnN0YWxsZWQgYW5kIHJlcXVpcmVkIGxpYnJhcmllcyBjYW4gYmUgbG9hZGVkOg0KDQotICAgaW5zdGFsbC5wYWNrYWdlcygicGtnYnVpbGQiKSAvLyBwa2didWlsZDo6Y2hlY2tfYnVpbGRfdG9vbHMoKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikNCi0gICBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIk5hbm9zdHJpbmctQmlvc3RhdHMvTmFub1N0cmluZ05DVG9vbHMiKQ0KLSAgIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiTmFub3N0cmluZy1CaW9zdGF0cy9HZW9teFRvb2xzIiwgcmVmID0NCiAgICAiZGV2IikNCi0gICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiR2VvTXhXb3JrZmxvd3MiKQ0KLSAgIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiRGF2aXNMYWJvcmF0b3J5L3N0YW5kUiIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIlNwYXRpYWxEZWNvbiIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIkdTVkEiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQotICAgaW5zdGFsbC5wYWNrYWdlcygiRFQiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoIm1zaWdkYnIiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoImRpZ2VzdCIpDQotICAgaW5zdGFsbC5wYWNrYWdlcygicm1hcmtkb3duIikNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCJrYWJsZSIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIkVCSW1hZ2UiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoJ3NjYXR0ZXJtb3JlJykNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCdwYmFwcGx5JykNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCdwbG90cml4JykNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCdnZ3RleHQnKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoJ1JCaW9Gb3JtYXRzJykNCi0gICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiYW9sZXMvUkJpb0Zvcm1hdHMiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoIkM6L1VzZXJzL2luaWptYW4vRG93bmxvYWRzL1NwYXRpYWxPbWljc092ZXJsYXktMC45OS4xMy1iZXRhLnRhci5neiIsDQogICAgZGVwZW5kZW5jaWVzID0gVFJVRSwgcmVwb3MgPSBOVUxMKQ0KLSAgIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiRGF2aXNMYWJvcmF0b3J5L3N0YW5kUiIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoImNsdXN0ZXJQcm9maWxlciIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoInBhdGh2aWV3IikNCg0KYGBge3IgbG9hZF9saWJyYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCiNsb2FkIGxpYnJhcmllcw0KDQojc3RhY2tfc2l6ZSA8LSBnZXRPcHRpb24oInBhbmRvYy5zdGFjay5zaXplIiwgZGVmYXVsdCA9ICI1MTJtIikNCiNvcHRpb25zKGphdmEucGFyYW1ldGVycyA9IGMoIi1YWDorVXNlQ29uY01hcmtTd2VlcEdDIiwgIi1YbXg4MTkybSwgLVhYOk1ldGFzcGFjZVNpemU9MTAyNE0iKSkNCm9wdGlvbnMoamF2YS5wYXJhbWV0ZXJzID0gYygiLVhYOitVc2VDb25jTWFya1N3ZWVwR0MiLCAiLVhteDgxOTJtIikpDQpsaWJyYXJ5KE5hbm9TdHJpbmdOQ1Rvb2xzKQ0KbGlicmFyeShHZW9teFRvb2xzKQ0KbGlicmFyeShHZW9NeFdvcmtmbG93cykNCmxpYnJhcnkoU3BhdGlhbERlY29uKQ0KbGlicmFyeShHU1ZBKSAjZm9yIHBhdGh3YXkgYW5hbHlzZXMNCmxpYnJhcnkobXNpZ2RicikgI2ZvciBtb2xlY3VsYXIgc2lnbmF0dXJlcyBpbiBwYXRod2F5IGFuYWx5c2VzDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dmb3JjZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoc2NhbGVzKSAjIGZvciBwZXJjZW50DQpsaWJyYXJ5KHJlc2hhcGUyKSAgIyBmb3IgbWVsdA0KbGlicmFyeShjb3dwbG90KSAgICMgZm9yIHBsb3RfZ3JpZA0KbGlicmFyeSh1bWFwKQ0KbGlicmFyeShSdHNuZSkNCmxpYnJhcnkocGhlYXRtYXApICAjIGZvciBwaGVhdG1hcA0KbGlicmFyeShnZ3JlcGVsKSANCmxpYnJhcnkoc2NhbGVzKSAjZm9yIGdncGxvdCBwZWF1ZG9sb2cgdG8gcHJldmVudCBlcnJvcnMgb24gbG9nKDApDQpsaWJyYXJ5KERUKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeShTcGF0aWFsT21pY3NPdmVybGF5KQ0KbGlicmFyeShndCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpDQpsaWJyYXJ5KGZnc2VhKQ0KbGlicmFyeShwYXRodmlldykNCmxpYnJhcnkocG5nKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KHZpcmlkaXMpDQoNCmxpYnJhcnkoc3RhbmRSKQ0KbGlicmFyeShTcGF0aWFsRXhwZXJpbWVudCkNCmxpYnJhcnkobGltbWEpDQpsaWJyYXJ5KGdnYWxsdXZpYWwpDQpsaWJyYXJ5KHNjYXRlcikNCg0KQmlvY01hbmFnZXI6Omluc3RhbGwoInN2YSIpDQpsaWJyYXJ5KHN2YSkNCg0KI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJwcmVwcm9jZXNzQ29yZSIpICNxdWFudGlsZSBub3JtDQpsaWJyYXJ5KHByZXByb2Nlc3NDb3JlKQ0KDQojaW5zdGFsbC5wYWNrYWdlcygiUG9seWNocm9tZSIpICNHZXQgY29sb3JzDQojbGlicmFyeShQb2x5Y2hyb21lKQ0KDQoNCmxpYnJhcnkoaW5mZXJjbnYpICMgQ05WDQpsaWJyYXJ5KCJhcGUiKQ0KbGlicmFyeSgiQmlvc3RyaW5ncyIpDQpsaWJyYXJ5KCJnZ3RyZWUiKQ0KbGlicmFyeSh0aWR5dHJlZSkNCmxpYnJhcnkoZ2dwdWJyKQ0KYGBgDQoNCiMgMSBsb2FkaW5nIGJhc2UgZmlsZXMNCg0KYGBge3IgbG9hZGluZ19iYXNlX2RhdGF9DQojIFJlZmVyZW5jZSB0aGUgbWFpbiBmb2xkZXIgJ2ZpbGUucGF0aCcgY29udGFpbmluZyB0aGUgc3ViLWZvbGRlcnMgd2l0aCBlYWNoIGRhdGEgZmlsZSB0eXBlOg0KZGF0YWRpcjwtZmlsZS5wYXRoKCJMOi9wa2xvb3N0ZXJtYW4vR2l0aHViL0RLRF9LaWRuZXkvIikNCmBgYA0KDQpUbyBsb2NhdGUgYSBzcGVjaWZpYyBmaWxlIHBhdGggcmVwbGFjZSB0aGUgYWJvdmUgbGluZSB3aXRoIGRhdGFkaXIgXDwtDQpmaWxlLnBhdGgoIlx+L0ZvbGRlci9TdWJGb2xkZXIvRGF0YUxvY2F0aW9uIikgcmVwbGFjZSB0aGUgRm9sZGVyLA0KU3ViRm9sZGVyLCBEYXRhTG9jYXRpb24gYXMgbmVlZGVkLiBUaGUgRGF0YUxvY2F0aW9uIGZvbGRlciBzaG91bGQNCmNvbnRhaW4gYSBkY2NzLCBwa2NzLCBhbmQgYW5ub3RhdGlvbiBmb2xkZXIgd2l0aCBlYWNoIHNldCBvZiBmaWxlcw0KcHJlc2VudCBhcyBuZWVkZWQgYXV0b21hdGljYWxseSBsaXN0IGZpbGVzIGluIGVhY2ggZGlyZWN0b3J5IGZvciB1c2UuDQoNCioqVGFrZSBjYXJlIHlvdSBpbXBvcnQgYSBjb2x1bW4gd2l0aCBudWNsZWkgY291bnQgc2VwYXJhdGVseSBpZiB5b3UNCndhbnQuKioNCg0KYGBge3IgcGFyc2VfZmlsZXN9DQpEQ0NGaWxlcyA8LSBkaXIoZmlsZS5wYXRoKGRhdGFkaXIsICJkY2NzIiksIHBhdHRlcm4gPSAiLmRjYyQiLA0KICAgICAgICAgICAgICAgIGZ1bGwubmFtZXMgPSBUUlVFLCByZWN1cnNpdmUgPSBUUlVFKQ0KUEtDRmlsZXMgPC0gZGlyKGZpbGUucGF0aChkYXRhZGlyLCAicGtjcyIpLCBwYXR0ZXJuID0gIi5wa2MkIiwNCiAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSwgcmVjdXJzaXZlID0gVFJVRSkNClNhbXBsZUFubm90YXRpb25GaWxlIDwtDQogIGRpcihmaWxlLnBhdGgoZGF0YWRpciwgImFubm90YXRpb24iKSwgcGF0dGVybiA9ICJeW15+XSIsDQogICAgICBmdWxsLm5hbWVzID0gVFJVRSwgcmVjdXJzaXZlID0gVFJVRSkNCmBgYA0KDQojIDIgbG9hZCBkYXRhDQoNCmBgYHtyIGxvYWRfZGF0YX0NCkRhdGEgPC0NCiAgcmVhZE5hbm9TdHJpbmdHZW9NeFNldChkY2NGaWxlcyA9IERDQ0ZpbGVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBrY0ZpbGVzID0gUEtDRmlsZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgcGhlbm9EYXRhRmlsZSA9IFNhbXBsZUFubm90YXRpb25GaWxlLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YVNoZWV0ID0gIlRlbXBsYXRlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGFEY2NDb2xOYW1lID0gIlNhbXBsZV9JRCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgcHJvdG9jb2xEYXRhQ29sTmFtZXMgPSBjKCJhb2kiLCAicm9pIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgZXhwZXJpbWVudERhdGFDb2xOYW1lcyA9IGMoInBhbmVsIikpDQoNCiNzYXZlIGRhdGEgdG8gcHJldmVudCBsb2FkaW5nIHRpbWUgZm9yIHJldGFrZXMNCiNzYXZlRGF0YTwtRGF0YQ0KI0RhdGE8LXNhdmVEYXRhDQoNCiNjaGFuZ2UgRGF0YSBjb2x1bW4gbmFtZXMgYW5kIG1hbnVhbCBjb3JyZWN0aW9uIG9mIHNwZWxsaW5nIGVycm9ycw0KRGF0YUBwaGVub0RhdGFAZGF0YVtbInNsaWRlX25hbWUiXV08LURhdGFAcGhlbm9EYXRhQGRhdGFbWyJzbGlkZSBuYW1lIl1dDQpEYXRhQHBoZW5vRGF0YUBkYXRhW1sic2xpZGUgbmFtZSJdXTwtICBOVUxMDQoNCg0KIysxIHJlZmVyZW5jZXMgdGhlIHNsaWRlIG5hbWUgY29sdW1uDQphbm5fc2l6ZTwtbGVuZ3RoKGNvbG5hbWVzKERhdGFAcGhlbm9EYXRhQGRhdGEpW2dyZXBsKCJBTk4iLGNvbG5hbWVzKERhdGFAcGhlbm9EYXRhQGRhdGEpKV0pKzEgDQphbm5fbmFtZXM8LWMoY29sbmFtZXMoRGF0YUBwaGVub0RhdGFAZGF0YSlbZ3JlcGwoIkFOTiIsY29sbmFtZXMoRGF0YUBwaGVub0RhdGFAZGF0YSkpXSwic2xpZGVfbmFtZSIpDQoNCiMgRmVlbCBmcmVlIHRvIGNoYW5nZSB0aGUgb3JkZXIgb2Ygd2hpY2ggY29sb3JzIGFyZSBhcHBvaW50ZWQuDQpjb2xvcjwtYygiI0EzNDlBNCIsICIjRkZGRjMzIiwgIiNFNzI5OEEiLCAiIzA5MTgzMyIsICIjMUI5RTc3IiwgIiNEOTVGMDIiLCAiIzc1NzBCMyIsICAiIzY2QTYxRSIsICIjRTZBQjAyIiwgIiM4REQzQzciLCAiIzlGMDAwRiIsICIjQkVCQURBIiwgIiNGQjgwNzIiLCAiIzgwQjFEMyIsICIjRkRCNDYyIiwgIiNCM0RFNjkiLCAiI0ZDQ0RFNSIsICIjRDlEOUQ5IiwgIiNCQzgwQkQiLCAiI0NDRUJDNSIsICIjRkZFRDZGIiwgIiMzNzdFQjgiLCAiIzk4NEVBMyIsICIjNERBRjRBIiwgIiNGRjcxQ0UiLCAiI0ZGN0YwMCIsICIjQTZDRUUzIiwgIiMxRjc4QjQiLCAiI0IyREY4QSIsICIjMzNBMDJDIiwgIiNGQjlBOTkiLCAiI0UzMUExQyIsICIjRkRCRjZGIiwgIiNDQUIyRDYiLCAiIzZBM0Q5QSIsICIjRkZGRjk5IiwgIiNCMTU5MjgiKQ0KDQojIFVzZSBjb3VudCBhbmQgY291bnRfbWF4IHRvIHNldCB0aGUgcmFuZ2Ugb2YgY29sb3IgbmVlZGVkIGZvciBlYWNoIGNvbHVtbi4NCiMgV2l0aCBzZXROYW1lcygpIHRoZSBjb2xvciBjb2RlIGlzIGxpbmtlZCB0byBlYWNoIHVuaXF1ZSBpbmRpdmlkdWFsIHZhbHVlIG9mIGVhY2ggY29sdW1uLg0KY291bnQ9MQ0KY29sb3JfbGlzdCA9IGxpc3QoKQ0KZm9yIChhbm4gaW4gYW5uX25hbWVzKSB7DQogIGNvdW50X21heCA9IGNvdW50K2xlbmd0aCh1bmlxdWUoRGF0YUBwaGVub0RhdGFAZGF0YVtbYW5uXV0pKS0xDQogIGNvbG9yX2xpc3RbW2Fubl1dPC1zZXROYW1lcyhjb2xvcltjb3VudDpjb3VudF9tYXhdLCB1bmlxdWUoRGF0YUBwaGVub0RhdGFAZGF0YVtbYW5uXV0pKQ0KICBjb3VudD1jb3VudF9tYXgrMQ0KfQ0KYGBgDQoNCmBgYHtyIGNvbG9yIHRhYmxlfQ0KdmFsdWVfbmFtZXMgPSBjKCkNCmZvciAoYW5uIGluIGFubl9uYW1lcykgew0KICB2YWx1ZV9uYW1lcyA8LSBjKHZhbHVlX25hbWVzLCB1bmlxdWUoRGF0YUBwaGVub0RhdGFAZGF0YVtbYW5uXV0pKQ0KfQ0KDQpjb2xvcl9kZiA8LSBhcy5kYXRhLmZyYW1lKHZhbHVlX25hbWVzKQ0KY29sb3JfZGYkY29sb3IgPC0gY29sb3JbMDpsZW5ndGgodmFsdWVfbmFtZXMpXQ0KDQpjb2xvcl9kZiAlPiUgDQogIG11dGF0ZShjb2xvciA9IGZjdF9pbm9yZGVyKGNvbG9yKSkgfD4gDQogIGd0KCkgJT4lIA0KICBkYXRhX2NvbG9yKGNvbHVtbnMgPSBjb2xvciwgY29sb3JzID0gYXMuY2hhcmFjdGVyKGNvbG9yKSkgJT4lDQogIHRhYl9vcHRpb25zKGNvbnRhaW5lci5oZWlnaHQgPSA1MDApDQpgYGANCg0KYGBge3J9DQpwYXN0ZSgiUmVhZHMgZnJvbSBmb2xsb3dpbmcgcnVucyB1c2VkOiAiLHVuaXF1ZShwRGF0YShwcm90b2NvbERhdGEoRGF0YSkpJFNlcVNldElkKSkNCmBgYA0KDQojIDMgU3R1ZHkgZGVzaWduDQoNCmBgYHtyIGFubm90YXRlfQ0KcGtjcyA8LSBhbm5vdGF0aW9uKERhdGEpDQptb2R1bGVzIDwtIGdzdWIoIi5wa2MiLCAiIiwgcGtjcykNCmthYmxlKGRhdGEuZnJhbWUoUEtDcyA9IHBrY3MsIG1vZHVsZXMgPSBtb2R1bGVzKSkNCmBgYA0KDQpTZWxlY3QgdGhlIGFubm90YXRpb25zIHdlIHdhbnQgdG8gc2hvdywgdXNlIFxgXGAgdG8gc3Vycm91bmQgY29sdW1uDQpuYW1lcyB3aXRoIHNwYWNlcyBvciBzcGVjaWFsIHN5bWJvbHMNCg0KYGBge3Igc2VsZWN0X2Fubm90YXRpb25zfQ0KI2NvdW50X21hdCA8LSBkcGx5cjo6Y291bnQocERhdGEoRGF0YSksIEFOTjEsQU5OMixzbGlkZV9uYW1lKQ0KY291bnRfbWF0IDwtIHBEYXRhKERhdGEpICU+JSBkcGx5cjo6Y291bnQocERhdGEoRGF0YSlbYyhhbm5fbmFtZXMpXSkNCmBgYA0KDQpTaW1wbGlmeSB0aGUgc2xpZGUgbmFtZXMgaWYgcmVxdWlyZWQNCg0KYGBge3Igc2ltcGxpZnlfbmFtZXN9DQojIGNvdW50X21hdCRzbGlkZV9uYW1lIDwtIGdzdWIoImRpc2Vhc2UiLCAiZCIsIGdzdWIoIm5vcm1hbCIsICJuIiwgY291bnRfbWF0JHNsaWRlX25hbWUpKQ0KI2NvdW50X21hdCRwYXRoX2FubiA8LSBnc3ViKCJpIiwgIiIsIGNvdW50X21hdCRwYXRoX2FubikgI2NvcnJlY3Rpbmcgc3BlbGxpbmcgZXJyb3INCmBgYA0KDQpHYXRoZXIgdGhlIGRhdGEgYW5kIHBsb3QgaW4gb3JkZXI6IGNsYXNzLCBzbGlkZSBuYW1lLCByZWdpb24sIHNlZ21lbnQNCg0KYGBge3IgZ2F0aGVyX2RhdGF9DQp0ZXN0X2dyIDwtIGdhdGhlcl9zZXRfZGF0YShjb3VudF9tYXQsIDE6YWxsX29mKGFubl9zaXplKSkNCnRlc3RfZ3IkeCA8LSBmYWN0b3IodGVzdF9nciR4LCBsYWJlbHMgPSBhbm5fbmFtZXMpDQpgYGANCg0KUGxvdCBTYW5rZXkNCg0KYGBge3IgU2Fua2V5X3Bsb3QsIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTExfQ0KZ2dwbG90KHRlc3RfZ3IsIGhlaWdodCA9IDEwLCB3aWR0aCA9IDEwLCBhZXMoeCwgaWQgPSBpZCwgc3BsaXQgPSB5LCB2YWx1ZSA9IG4pKSArDQogIGdlb21fcGFyYWxsZWxfc2V0cyhhZXMoZmlsbCA9IEFOTjIpLCBhbHBoYSA9IDAuNSwgYXhpcy53aWR0aCA9IDAuMSkgKw0KICBnZW9tX3BhcmFsbGVsX3NldHNfYXhlcyhheGlzLndpZHRoID0gMC4yKSArDQogIGdlb21fcGFyYWxsZWxfc2V0c19sYWJlbHMoY29sb3IgPSAid2hpdGUiLCBzaXplID0gNSkgKw0KICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDEyKSArIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKSArDQogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24oMCkpICsgDQogIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gZXhwYW5zaW9uKDApKSArDQogIGxhYnMoeCA9ICIiLCB5ID0gIiIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNvbG9yX2xpc3QkQU5OMikgKw0KICBhbm5vdGF0ZShnZW9tID0gInNlZ21lbnQiLCB4ID0gNC4yNSwgeGVuZCA9IDQuMjUsDQogICAgICAgICAgIHkgPSAxMCwgeWVuZCA9IDYxLCBsd2QgPSAyKSArDQogIGFubm90YXRlKGdlb20gPSAidGV4dCIsIHggPSA0LjE5LCB5ID0gMjUsIGFuZ2xlID0gOTAsIHNpemUgPSA1LA0KICAgICAgICAgICBoanVzdCA9IDAuNSwgbGFiZWwgPSAiNTAgc2VnbWVudHMiKQ0KYGBgDQoNCiMgNCBRQyAmIFByZS1wcm9jZXNzaW5nDQoNClNoaWZ0IGNvdW50cyB0byBvbmUNCg0KYGBge3Igc2hpZnRfY291bnRzfQ0KI3NoaWZ0IGFueSBleHByZXNzaW9uIGNvdW50cyB3aXRoIGEgdmFsdWUgb2YgMCB0byAxIHRvIGVuYWJsZSBpbiBkb3duc3RyZWFtIHRyYW5zZm9ybWF0aW9ucy4NCkRhdGEgPC0gc2hpZnRDb3VudHNPbmUoRGF0YSwgdXNlREFMb2dpYyA9IFRSVUUpDQpgYGANCg0KIyA0LjEgU2VnbWVudCBRQw0KDQpXZSBmaXJzdCBhc3Nlc3Mgc2VxdWVuY2luZyBxdWFsaXR5IGFuZCBhZGVxdWF0ZSB0aXNzdWUgc2FtcGxpbmcgZm9yDQpldmVyeSBST0kvQU9JIHNlZ21lbnQuDQoNCkV2ZXJ5IFJPSS9BT0kgc2VnbWVudCB3aWxsIGJlIHRlc3RlZCBmb3I6DQoNClJhdyBzZXF1ZW5jaW5nIHJlYWRzOiBzZWdtZW50cyB3aXRoIFw+MTAwMCByYXcgcmVhZHMgYXJlIHJlbW92ZWQuICUNCkFsaWduZWQsJSBUcmltbWVkLCBvciAlIFN0aXRjaGVkIHNlcXVlbmNpbmcgcmVhZHM6IHNlZ21lbnRzIGJlbG93IFx+ODAlDQpmb3Igb25lIG9yIG1vcmUgb2YgdGhlc2UgUUMgcGFyYW1ldGVycyBhcmUgcmVtb3ZlZC4gJSBTZXF1ZW5jaW5nDQpzYXR1cmF0aW9uIChbMS1kZWR1cGxpY2F0ZWQgcmVhZHMvYWxpZ25lZCByZWFkc10lKTogc2VnbWVudHMgYmVsb3cgXH41MCUNCnJlcXVpcmUgYWRkaXRpb25hbCBzZXF1ZW5jaW5nIHRvIGNhcHR1cmUgZnVsbCBzYW1wbGUgZGl2ZXJzaXR5IGFuZCBhcmUNCm5vdCB0eXBpY2FsbHkgYW5hbHl6ZWQgdW50aWwgaW1wcm92ZWQuIE5lZ2F0aXZlIENvdW50OiB0aGlzIGlzIHRoZQ0KZ2VvbWV0cmljIG1lYW4gb2YgdGhlIHNldmVyYWwgdW5pcXVlIG5lZ2F0aXZlIHByb2JlcyBpbiB0aGUgR2VvTXggcGFuZWwNCnRoYXQgZG8gbm90IHRhcmdldCBtUk5BIGFuZCBlc3RhYmxpc2ggdGhlIGJhY2tncm91bmQgY291bnQgbGV2ZWwgcGVyDQpzZWdtZW50OyBzZWdtZW50cyB3aXRoIGxvdyBuZWdhdGl2ZSBjb3VudHMgKDEtMTApIGFyZSBub3QgbmVjZXNzYXJpbHkNCnJlbW92ZWQgYnV0IG1heSBiZSBzdHVkaWVkIGNsb3NlciBmb3IgbG93IGVuZG9nZW5vdXMgZ2VuZSBzaWduYWwgYW5kL29yDQppbnN1ZmZpY2llbnQgdGlzc3VlIHNhbXBsaW5nLiBObyBUZW1wbGF0ZSBDb250cm9sIChOVEMpIGNvdW50OiB2YWx1ZXMNClw+MSwwMDAgY291bGQgaW5kaWNhdGUgY29udGFtaW5hdGlvbiBmb3IgdGhlIHNlZ21lbnRzIGFzc29jaWF0ZWQgd2l0aA0KdGhpcyBOVEM7IGhvd2V2ZXIsIGluIGNhc2VzIHdoZXJlIHRoZSBOVEMgY291bnQgaXMgYmV0d2VlbiAxLDAwMC0NCjEwLDAwMCwgdGhlIHNlZ21lbnRzIG1heSBiZSB1c2VkIGlmIHRoZSBOVEMgZGF0YSBpcyB1bmlmb3JtbHkgbG93IChlLmcuDQowLTIgY291bnRzIGZvciBhbGwgcHJvYmVzKS4gTnVjbGVpOiBcPjEwMCBudWNsZWkgcGVyIHNlZ21lbnQgaXMNCmdlbmVyYWxseSByZWNvbW1lbmRlZDsgaG93ZXZlciwgdGhpcyBjdXRvZmYgaXMgaGlnaGx5IHN0dWR5L3Rpc3N1ZQ0KZGVwZW5kZW50IGFuZCBtYXkgbmVlZCB0byBiZSByZWR1Y2VkOyB3aGF0IGlzIG1vc3QgaW1wb3J0YW50IGlzDQpjb25zaXN0ZW5jeSBpbiB0aGUgbnVjbGVpIGRpc3RyaWJ1dGlvbiBmb3Igc2VnbWVudHMgd2l0aGluIHRoZSBzdHVkeS4NCkFyZWE6IGdlbmVyYWxseSBjb3JyZWxhdGVzIHdpdGggbnVjbGVpOyBhIHN0cmljdCBjdXRvZmYgaXMgbm90IGdlbmVyYWxseQ0KYXBwbGllZCBiYXNlZCBvbiBhcmVhLg0KDQojIDQuMS4xIFNlbGVjdCBTZWdtZW50IFFDDQoNCkZpcnN0LCB3ZSBzZWxlY3QgdGhlIFFDIHBhcmFtZXRlciBjdXRvZmZzLCBhZ2FpbnN0IHdoaWNoIG91ciBST0kvQU9JDQpzZWdtZW50cyB3aWxsIGJlIHRlc3RlZCBhbmQgZmxhZ2dlZCBhcHByb3ByaWF0ZWx5LiBXZSBoYXZlIHNlbGVjdGVkIHRoZQ0KYXBwcm9wcmlhdGUgc3R1ZHktc3BlY2lmaWMgcGFyYW1ldGVycyBmb3IgdGhpcyBzdHVkeS4gTm90ZTogdGhlIGRlZmF1bHQNClFDIHZhbHVlcyByZWNvbW1lbmRlZCBhYm92ZSBhcmUgYWR2aXNlZCB3aGVuIHN1cnZleWluZyBhIG5ldyBkYXRhc2V0IGZvcg0KdGhlIGZpcnN0IHRpbWUuDQoNCkRlZmF1bHQgUUMgY3V0b2ZmcyBhcmUgY29tbWVudGVkIGluICgpIGFkamFjZW50IHRvIHRoZSByZXNwZWN0aXZlDQpwYXJhbWV0ZXJzIHN0dWR5LXNwZWNpZmljIHZhbHVlcyB3ZXJlIHNlbGVjdGVkIGFmdGVyIHZpc3VhbGl6aW5nIHRoZSBRQw0KcmVzdWx0cyBpbiBtb3JlIGRldGFpbCBiZWxvdw0KDQpgYGB7ciBzZXRfUUNfcGFyYW1zfQ0KUUNfcGFyYW1zIDwtDQogIGxpc3QobWluU2VnbWVudFJlYWRzID0gMTAwMCwgIyBNaW5pbXVtIG51bWJlciBvZiByZWFkcyAoMTAwMCkNCiAgICAgICBwZXJjZW50VHJpbW1lZCA9IDgwLCAgICAjIE1pbmltdW0gJSBvZiByZWFkcyB0cmltbWVkICg4MCUpDQogICAgICAgcGVyY2VudFN0aXRjaGVkID0gODAsICAgIyBNaW5pbXVtICUgb2YgcmVhZHMgc3RpdGNoZWQgKDgwJSkNCiAgICAgICBwZXJjZW50QWxpZ25lZCA9IDc1LCAgICAjIE1pbmltdW0gJSBvZiByZWFkcyBhbGlnbmVkICg4MCUpDQogICAgICAgcGVyY2VudFNhdHVyYXRpb24gPSA1MCwgIyBNaW5pbXVtIHNlcXVlbmNpbmcgc2F0dXJhdGlvbiAoNTAlKQ0KICAgICAgIG1pbk5lZ2F0aXZlQ291bnQgPSAxLCAgICMgTWluaW11bSBuZWdhdGl2ZSBjb250cm9sIGNvdW50cyAoMTApDQogICAgICAgbWF4TlRDQ291bnQgPSA5MDAwLCAgICAgIyBNYXhpbXVtIGNvdW50cyBvYnNlcnZlZCBpbiBOVEMgd2VsbCAoMTAwMCkNCiAgICAgICBtaW5OdWNsZWkgPSAyMCwgICAgICAgICMgTWluaW11bSAjIG9mIG51Y2xlaSBlc3RpbWF0ZWQgKDEwMCkNCiAgICAgICBtaW5BcmVhID0gMTAwMCkgICAgICAgICAjIE1pbmltdW0gc2VnbWVudCBhcmVhICg1MDAwKQ0KRGF0YSA8LQ0KICBzZXRTZWdtZW50UUNGbGFncyhEYXRhLCBxY0N1dG9mZnMgPSBRQ19wYXJhbXMpICAgICAgICANCg0KY2F0KCJwcmUtUUMgZmVhdHVyZXM6IiwgZGltKERhdGEpWzFdLCAiXG5wcmUtUUMgc2FtcGxlczoiLCBkaW0oRGF0YSlbMl0pDQoNCiNUYWJsZSBmb3IgY2xhcmlmaWNhdGlvbg0KUUNwYXJhbXNfZGYgPC0gZGF0YS5mcmFtZSAoDQogIGl0ZW1zID0gYygibWluU2VnbWVudFJlYWRzIiwicGVyY2VudFRyaW1tZWQiLCJwZXJjZW50U3RpdGNoZWQiLCJwZXJjZW50QWxpZ25lZCIsInBlcmNlbnRTYXR1cmF0aW9uIiwNCiAgICAgICAgICAgICJtaW5OZWdhdGl2ZUNvdW50IiwibWF4TlRDQ291bnQiLCJtaW5OdWNsZWkiLCJtaW5BcmVhIiksDQogIGRlZmF1bHRzID0gYygxMDAwLDgwLDgwLDgwLDUwLDEwLDEwMDAsMTAwLDUwMDApLA0KICBhY3R1YWwgPSBjKFFDX3BhcmFtcyRtaW5TZWdtZW50UmVhZHMsUUNfcGFyYW1zJHBlcmNlbnRUcmltbWVkLFFDX3BhcmFtcyRwZXJjZW50U3RpdGNoZWQsUUNfcGFyYW1zJHBlcmNlbnRBbGlnbmVkLFFDX3BhcmFtcyRwZXJjZW50U2F0dXJhdGlvbixRQ19wYXJhbXMkbWluTmVnYXRpdmVDb3VudCxRQ19wYXJhbXMkbWF4TlRDQ291bnQsUUNfcGFyYW1zJG1pbk51Y2xlaSxRQ19wYXJhbXMkbWluQXJlYSkNCikNCg0KZGF0YXRhYmxlKFFDcGFyYW1zX2RmLCByb3duYW1lcz1GQUxTRSwNCiAgICAgICAgICBjYXB0aW9uID0gIlFDIHRocmVzaG9sZHMiLA0KICAgICAgICAgIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsIG9wdGlvbnMgPSBsaXN0ICgNCiAgICAgICAgICAgIGRvbSA9ICdCZnRyaXAnLA0KICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpDQogICAgICAgICAgKQ0KKQ0KDQpgYGANCg0KQ29sbGF0ZSBRQyBSZXN1bHRzDQoNCmBgYHtyIGNvbGxhdGVfUUNfcmVzdWx0c30NClFDUmVzdWx0cyA8LSBwcm90b2NvbERhdGEoRGF0YSlbWyJRQ0ZsYWdzIl1dDQpmbGFnX2NvbHVtbnMgPC0gY29sbmFtZXMoUUNSZXN1bHRzKQ0KUUNfU3VtbWFyeSA8LSBkYXRhLmZyYW1lKFBhc3MgPSBjb2xTdW1zKCFRQ1Jlc3VsdHNbLCBmbGFnX2NvbHVtbnNdKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBXYXJuaW5nID0gY29sU3VtcyhRQ1Jlc3VsdHNbLCBmbGFnX2NvbHVtbnNdKSkNCg0KUUNSZXN1bHRzJFFDU3RhdHVzIDwtIGFwcGx5KFFDUmVzdWx0cywgMUwsIGZ1bmN0aW9uKHgpIHsNCiAgaWZlbHNlKHN1bSh4KSA9PSAwTCwgIlBBU1MiLCAiV0FSTklORyIpDQp9KQ0KDQpRQ19TdW1tYXJ5WyJUT1RBTCBGTEFHUyIsIF0gPC0NCiAgYyhzdW0oUUNSZXN1bHRzWywgIlFDU3RhdHVzIl0gPT0gIlBBU1MiKSwNCiAgICBzdW0oUUNSZXN1bHRzWywgIlFDU3RhdHVzIl0gPT0gIldBUk5JTkciKSkNCg0KY29sX2J5IDwtICJBTk4xIg0KY29sX2J5X3BsYXRlIDwtICJQbGF0ZV9JRCINCmBgYA0KDQojIDQuMiBHcmFwaGljYWwgc3VtbWFyaWVzIG9mIFFDIHN0YXRpc3RpY3Mgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KVXNlIHRoZSB0YWItbWVudSB0byBuYXZpZ2F0ZSENCg0KYGBge3IgUUNfcGxvdHRpbmd9DQpRQ19oaXN0b2dyYW0gPC0gZnVuY3Rpb24oYXNzYXlfZGF0YSA9IE5VTEwsDQogICAgICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbiA9IE5VTEwsDQogICAgICAgICAgICAgICAgICAgICAgICAgZmlsbF9ieSA9IE5VTEwsDQogICAgICAgICAgICAgICAgICAgICAgICAgdGhyID0gTlVMTCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZV90cmFucyA9IE5VTEwpIHsNCiAgcGx0IDwtIGdncGxvdChhc3NheV9kYXRhLA0KICAgICAgICAgICAgICAgIGFlc19zdHJpbmcoeCA9IHBhc3RlMCgidW5saXN0KGAiLCBhbm5vdGF0aW9uLCAiYCkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmaWxsX2J5KSkgKw0KICAgIGdlb21faGlzdG9ncmFtKGJpbnMgPSA1MCkgKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHRociwgbHR5ID0gImRhc2hlZCIsIGNvbG9yID0gImJsYWNrIikgKw0KICAgIHRoZW1lX2J3KCkgKyBndWlkZXMoZmlsbCA9ICJub25lIikgKw0KICAgIGZhY2V0X3dyYXAoYXMuZm9ybXVsYShwYXN0ZSgifiIsIGZpbGxfYnkpKSwgbnJvdyA9IDQpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y29sb3JfbGlzdCRBTk4xKSArDQogICAgbGFicyh4ID0gYW5ub3RhdGlvbiwgeSA9ICJzZWdtZW50cywgIyIsIHRpdGxlID0gYW5ub3RhdGlvbikNCiAgaWYoIWlzLm51bGwoc2NhbGVfdHJhbnMpKSB7DQogICAgcGx0IDwtIHBsdCArDQogICAgICBzY2FsZV94X2NvbnRpbnVvdXModHJhbnMgPSBzY2FsZV90cmFucykNCiAgfQ0KICBwbHQNCn0NCmBgYA0KDQojIyBUcmltbWVkDQoNCmBgYHtyfQ0KUUNfaGlzdG9ncmFtKHNEYXRhKERhdGEpLCAiVHJpbW1lZCAoJSkiLCBjb2xfYnksIFFDX3BhcmFtcyRwZXJjZW50VHJpbW1lZCkNCmBgYA0KDQojIyBTdGljaGVkICglKQ0KDQpgYGB7cn0NClFDX2hpc3RvZ3JhbShzRGF0YShEYXRhKSwgIlN0aXRjaGVkICglKSIsIGNvbF9ieSwgUUNfcGFyYW1zJHBlcmNlbnRTdGl0Y2hlZCkNCmBgYA0KDQojIyBBbGlnbmVkICglKQ0KDQpgYGB7cn0NClFDX2hpc3RvZ3JhbShzRGF0YShEYXRhKSwgIkFsaWduZWQgKCUpIiwgY29sX2J5LFFDX3BhcmFtcyRwZXJjZW50QWxpZ25lZCkNCmBgYA0KDQojIyBTZXF1ZW5jaW5nIFNhdHVyYXRpb24gKCUpIHsuYWN0aXZlfQ0KDQpgYGB7cn0NClFDX2hpc3RvZ3JhbShzRGF0YShEYXRhKSwgIlNhdHVyYXRlZCAoJSkiLCBjb2xfYnksIFFDX3BhcmFtcyRwZXJjZW50U2F0dXJhdGlvbikgKw0KICBsYWJzKHRpdGxlID0gIlNlcXVlbmNpbmcgU2F0dXJhdGlvbiAoJSkiLA0KICAgICAgIHggPSAiU2VxdWVuY2luZyBTYXR1cmF0aW9uICglKSIpDQpgYGANCg0KIyMgQXJlYQ0KDQpgYGB7cn0NClFDX2hpc3RvZ3JhbShzRGF0YShEYXRhKSwgImFyZWEiLCBjb2xfYnksIFFDX3BhcmFtcyRtaW5BcmVhLCBzY2FsZV90cmFucyA9ICJsb2cxMCIpDQpgYGANCg0KIyMgTnVjbGVpIGNvdW50DQoNCmBgYHtyfQ0KUUNfaGlzdG9ncmFtKHNEYXRhKERhdGEpLCAibnVjbGVpIiwgY29sX2J5LCBRQ19wYXJhbXMkbWluTnVjbGVpKQ0KYGBgDQoNCiMjIER1cGxpY2F0aW9uUmF0ZQ0KDQpgYGB7cn0NCmdncGxvdChwRGF0YShwcm90b2NvbERhdGEoRGF0YSkpLA0KICAgICAgIGFlcyh4ID0gUGxhdGVfSUQsIGZpbGw9UGxhdGVfSUQsDQogICAgICAgICAgeSA9IChEZWR1cGxpY2F0ZWRSZWFkcy9SYXcpKSkgKw0KICBnZW9tX3Zpb2xpbigpICsNCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAuMikgKw0KICBsYWJzKHkgPSAiRGVkdXBsaWNhdGVkIC8gUmF3IHJlYWRzIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArDQogIHRoZW1lX2J3KCkNCmBgYA0KDQojIyBOZWdwcm9iZXMgdnMgRW5kb2dlbm91cw0KDQpgYGB7ciBwbG90X25lZ3Byb2JlX2RhdGEsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTV9DQp0bXBfdGFyZ2V0X0RhdGEgPC0gYWdncmVnYXRlQ291bnRzKERhdGEpDQoNCiNnZXQgbmVnYXRpdmUgcHJvYmUgZGF0YQ0KbmVnczwtc3Vic2V0KHRtcF90YXJnZXRfRGF0YSxDb2RlQ2xhc3M9PSJOZWdhdGl2ZSIpDQoNCnAxPC1nZ3Bsb3QocERhdGEobmVncyksDQogICAgICAgYWVzKHggPSBBTk4yLCBmaWxsID0gQU5OMiwNCiAgICAgICAgICB5ID0gYXNzYXlEYXRhRWxlbWVudChuZWdzLCBlbHQgPSAiZXhwcnMiKSkpICsNCiAgZ2VvbV92aW9saW4oKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gLjIpICsNCiAgbGFicyh5ID0gIk5lZ2F0aXZlIHByb2JlcyBFeHByZXNzaW9uIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygxLDMwMDApLCB0cmFucyA9ICJsb2cyIikgKw0KICB0aGVtZV9idygpICsgY29vcmRfZmxpcCgpDQoNCg0KIyBnZXQgZW5kb2dlbm91cyBwcm9iZSBkYXRhDQplbmQ8LXN1YnNldCh0bXBfdGFyZ2V0X0RhdGEsQ29kZUNsYXNzPT0iRW5kb2dlbm91cyIpDQoNCnAyPC1nZ3Bsb3QocERhdGEoZW5kKSwNCiAgICAgICBhZXMoeCA9IEFOTjIsIGZpbGwgPSBBTk4yLA0KICAgICAgICAgICB5ID0gY29sTWVhbnMoYXNzYXlEYXRhRWxlbWVudChlbmQsIGVsdCA9ICJleHBycyIpKSkpICsNCiAgZ2VvbV92aW9saW4oKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gLjIpICsNCiAgbGFicyh5ID0gIkVuZG9nZW5vdXMgcHJvYmVzIEV4cHJlc3Npb24gKG1lYW4pIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygxLDMwMDApLHRyYW5zID0gImxvZzIiKSArDQogIHRoZW1lX2J3KCkgKyBjb29yZF9mbGlwKCkNCg0KcGwgPC1saXN0KHAxLHAyKQ0KcGxvdF9ncmlkKHBsb3RsaXN0PXBsLCBucm93PTIsIGFsaWduPSd2JykNCg0KYGBgDQoNCiMjIE5lZ19wcm9iZSByZWFkcyBjb21wYXJlZCB0byByYXdfcmVhZHMNCg0KYGBge3J9DQoNCiMgbWFrZSBiYWNrZ3JvdW5kIHRvdGFsIG5lZyBwcm9iZSBjb3VudA0KZmRhdGFfZGY8LWZEYXRhKERhdGEpDQpuZWdwcm9iZXNuYW1lczwtcm93bmFtZXMoZmRhdGFfZGZbZmRhdGFfZGYkTmVnYXRpdmU9PVRSVUUsXSkNCnRlbXBfZXhwPC1hc3NheURhdGFFbGVtZW50KERhdGEsZWx0PSdleHBycycpDQpuZWdwcm9iZV9leHByX2ZkPC10ZW1wX2V4cFtyb3duYW1lcyh0ZW1wX2V4cCkgJWluJSBuZWdwcm9iZXNuYW1lcyxdDQp0b3RfbmVnX2N0cmxfcmVhZHM8LWNvbFN1bXMobmVncHJvYmVfZXhwcl9mZCkNCnRvdF9kZWR1cF9yZWFkczwtcERhdGEocHJvdG9jb2xEYXRhKERhdGEpKSREZWR1cGxpY2F0ZWRSZWFkcw0KDQpkZjwtZGF0YS5mcmFtZSgnYW9pJz0gbmFtZXModG90X25lZ19jdHJsX3JlYWRzKSwndG90X2RlZHVwX3JlYWRzJyA9IGFzLm51bWVyaWModG90X2RlZHVwX3JlYWRzKSwndG90X25lZ19jdHJsX3JlYWRzJz1hcy5udW1lcmljKHRvdF9uZWdfY3RybF9yZWFkcykpDQpkZjwtbWVsdChkZixpZD0iYW9pIikNCmdncGxvdChkZixhZXMoZmlsbD12YXJpYWJsZSx5PXZhbHVlLHg9YW9pKSkgKyANCiAgZ2VvbV9iYXIocG9zaXRpb249ImlkZW50aXR5IixzdGF0PSJpZGVudGl0eSIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gbG9nMl90cmFucygpKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIixheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSxheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpKSAgICAgICAgICAgICAgICAgICAgIA0KIA0KYGBgDQoNCiMjIER1cGxpY2F0ZWQgcmVhZHMgdnMgQmFja2dyb3VuZA0KDQpgYGB7cn0NCiMgZ2V0IGRjYyBwZXIgcGxhdGUuIHN1bSBuZWdwcm9iZSBjb3VudHMvZGNjL3BsYXRlDQpnZ3Bsb3QocERhdGEocHJvdG9jb2xEYXRhKERhdGEpKSwNCiAgICAgICBhZXMoeCA9IFBsYXRlX0lELCBmaWxsPVBsYXRlX0lELA0KICAgICAgICAgIHkgPSBEZWR1cGxpY2F0ZWRSZWFkcykpICsNCiAgZ2VvbV92aW9saW4oKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gLjIpICsNCiAgbGFicyh5ID0gIkRlZHVwbGljYXRlZCAvIFJhdyByZWFkcyIpICsNCiAgc2NhbGVfeV9sb2cxMCgpKw0KICBnZW9tX2hsaW5lKGRhdGEgPXBEYXRhKHByb3RvY29sRGF0YShEYXRhKSkgLCANCiAgICAgICAgICAgYWVzKHlpbnRlcmNlcHQgPSBOVEMsIGNvbG91cj1QbGF0ZV9JRCkpICsNCiAgdGhlbWVfYncoKQ0KDQpgYGANCg0KIyMgRHVwbGljYXRlZCByZWFkcyB2cyBST0lhcmVhDQoNCmBgYHtyLCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD01fQ0KdGVtcF9kZjwtY2JpbmQocERhdGEoRGF0YSkscERhdGEocHJvdG9jb2xEYXRhKERhdGEpKSxkY2M9cm93bmFtZXMocERhdGEoRGF0YSkpKQ0KDQpnZ3Bsb3QodGVtcF9kZiwNCiAgICAgICBhZXMoeCA9IGRjYywgY29sb3VyPXNsaWRlX25hbWUsDQogICAgICAgICAgeSA9IChEZWR1cGxpY2F0ZWRSZWFkcy9hcmVhKSApKSArDQogIGdlb21fcG9pbnQoKSArDQogIHlsaW0oMCwyMCkgKyANCiAgbGFicyh5ID0gIkRlZHVwbGljYXRlZCByZWFkcyAvIFJPSSBhcmVhIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID02LCBhbmdsZT05MCwgaGp1c3Q9MSkgKQ0KDQpgYGANCg0KIyMgRHVwbGljYXRlZCByZWFkcyB2cyBudWNsZWkNCg0KYGBge3IsIGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTV9DQp0ZW1wX2RmPC1jYmluZChwRGF0YShEYXRhKSxwRGF0YShwcm90b2NvbERhdGEoRGF0YSkpLGRjYz1yb3duYW1lcyhwRGF0YShEYXRhKSkpDQoNCmdncGxvdCh0ZW1wX2RmLA0KICAgICAgIGFlcyh4ID0gZGNjLCBjb2xvdXI9c2xpZGVfbmFtZSwNCiAgICAgICAgICB5ID0gKERlZHVwbGljYXRlZFJlYWRzL251Y2xlaSkgKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICB5bGltKDAsNTApICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEFkanVzdCBwZXIgcHJvamVjdA0KICBsYWJzKHkgPSAiRGVkdXBsaWNhdGVkIHJlYWRzIC8gbnVjbGVpIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID02LCBhbmdsZT05MCwgaGp1c3Q9MSkgKQ0KDQpgYGANCg0KIyA0LjMgUHJvY2VzcyBOZWdhdGl2ZSBHZW9NZWFucw0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSBuZWdhdGl2ZSBnZW9tZXRyaWMgbWVhbnMgZm9yIGVhY2ggbW9kdWxlDQojIEl0IHdpbGwgc2hvdyBvbmx5IHRoZSBuZWdhdGl2ZSBwcm9iZXMgZ2VvbWVhbiwgc28gZXhwZWN0IGxlc3Mgc2VnbWVudHMuDQpuZWdhdGl2ZUdlb01lYW5zIDwtIA0KICBlc0J5KG5lZ2F0aXZlQ29udHJvbFN1YnNldChEYXRhKSwgDQogICAgICAgR1JPVVAgPSAiTW9kdWxlIiwgDQogICAgICAgRlVOID0gZnVuY3Rpb24oeCkgeyANCiAgICAgICAgIGFzc2F5RGF0YUFwcGx5KHgsIE1BUkdJTiA9IDIsIEZVTiA9IG5nZW9NZWFuLCBlbHQgPSAiZXhwcnMiKSANCiAgICAgICB9KSANCnByb3RvY29sRGF0YShEYXRhKVtbIk5lZ0dlb01lYW4iXV0gPC0gbmVnYXRpdmVHZW9NZWFucw0KDQpuZWdDb2xzIDwtIHBhc3RlMCgiTmVnR2VvTWVhbl8iLCBtb2R1bGVzKQ0KcERhdGEoRGF0YSlbLCBuZWdDb2xzXSA8LSBzRGF0YShEYXRhKVtbIk5lZ0dlb01lYW4iXV0NCmZvcihhbm4gaW4gbmVnQ29scykgew0KICBwbHQgPC0gUUNfaGlzdG9ncmFtKHBEYXRhKERhdGEpLCBhbm4sIGNvbF9ieSwgMiwgc2NhbGVfdHJhbnMgPSAibG9nMTAiKQ0KICBwcmludChwbHQpDQp9DQoNCg0KIyBEZXRhdGNoIG5lZ19nZW9tZWFuIGNvbHVtbnMgYWhlYWQgb2YgYWdncmVnYXRlQ291bnRzIGNhbGwNCg0KcERhdGEoRGF0YSkgPC0gcERhdGEoRGF0YSlbLCAhY29sbmFtZXMocERhdGEoRGF0YSkpICVpbiUgbmVnQ29sc10NCg0KYGBgDQoNClNob3cgYWxsIE5UQyB2YWx1ZXMsIEZyZXEgPSBcIyBvZiBTZWdtZW50cyB3aXRoIGEgZ2l2ZW4gTlRDIGNvdW50Og0KDQpgYGB7ciBRQ190YWJsZXN9DQpRQzwtc0RhdGEoRGF0YSkNCg0KbnRjX2RmIDwtUUNbLGMoInNsaWRlX25hbWUiLCJQbGF0ZV9JRCIsIk5UQ19JRCIsIk5UQyIpXQ0KdGVtcHRhYmxlPC1udGNfZGYgJT4lIGRwbHlyOjpjb3VudChudGNfZGYkc2xpZGVfbmFtZSxudGNfZGYkTlRDX0lELG50Y19kZiRQbGF0ZV9JRCxudGNfZGYkTlRDKQ0KY29sbmFtZXModGVtcHRhYmxlKSA8LSBjKCJTbGlkZV9uYW1lIiwiTlRDX0lEIiwiUGxhdGVfSUQiLCJOVENfY291bnQiLCJOdW1iZXJfb2Zfc2FtcGxlcyIpDQpkYXRhdGFibGUodGVtcHRhYmxlLCByb3duYW1lcyA9IEZBTFNFKQ0KDQoNCmthYmxlKHRhYmxlKE5UQ19Db3VudCA9IHNEYXRhKERhdGEpJE5UQyksIGNvbC5uYW1lcyA9IGMoIk5UQyBDb3VudCIsICIjIG9mIFNlZ21lbnRzIikpDQoNCmthYmxlKFFDX1N1bW1hcnksIGNhcHRpb24gPSAiUUMgU3VtbWFyeSBUYWJsZSBmb3IgZWFjaCBTZWdtZW50IikNCg0KZGF0YXRhYmxlKFFDX1N1bW1hcnksDQogICAgICAgICAgY2FwdGlvbiA9ICJBT0kgUUMgU3VtbWFyeSIsDQogICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgZG9tID0gJ0JmdHJpcCcsDQogICAgICAgICAgICBidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykNCiAgICAgICAgICApDQopDQpgYGANCg0KU2hvdyBBT0lzIHdoaWNoIGZhaWwgY3JpdGljYWwgUUNzLg0KDQpgYGB7ciBsaXN0X2ZhaWx1cmVzfQ0KUUM8LXNEYXRhKERhdGEpDQp1bmRlcnNhdDwtc3Vic2V0KFFDLCBgU2F0dXJhdGVkICglKWA8PSBRQ19wYXJhbXMkcGVyY2VudFNhdHVyYXRpb24pDQoNCmlmKG5yb3codW5kZXJzYXQpPiAwKSB7DQoNCmRhdGF0YWJsZShhZ2dyZWdhdGUodW5kZXJzYXQsIGJ5PWxpc3QodW5kZXJzYXQkU2FtcGxlSUQpLHBhc3RlLGNvbGxhcHNlPSI7IiksDQogICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgZG9tID0gJ0JmdHJpcCcsDQogICAgICAgICAgICBidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykNCiAgICAgICAgICApDQopfQ0KYGBgDQoNClN1YnNldHRpbmcgb3VyIGRhdGFzZXQgaGFzIHJlbW92ZWQgc2FtcGxlcyB3aGljaCBkaWQgbm90IHBhc3MgUUMNCg0KYGBge3Igc3Vic2V0dGluZ19RQ19mYWlsc30NCkRhdGEgPC0gRGF0YVssIFFDUmVzdWx0cyRRQ1N0YXR1cyA9PSAiUEFTUyJdDQpgYGANCg0KR2VuZXJhbGx5IGtlZXAgdGhlIHFjQ3V0b2ZmcyBwYXJhbWV0ZXJzIHVuY2hhbmdlZC4gU2V0DQpyZW1vdmVMb2NhbE91dGxpZXJzIHRvIEZBTFNFIGlmIHlvdSBkbyBub3Qgd2FudCB0byByZW1vdmUgbG9jYWwgb3V0bGllcnMNCg0KYGBge3IgcHJvY2Vzc19RQ30NCkRhdGEgPC0gc2V0QmlvUHJvYmVRQ0ZsYWdzKERhdGEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHFjQ3V0b2ZmcyA9IGxpc3QobWluUHJvYmVSYXRpbyA9IDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBlcmNlbnRGYWlsR3J1YmJzID0gMjApLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZW1vdmVMb2NhbE91dGxpZXJzID0gRkFMU0UpDQoNClByb2JlUUNSZXN1bHRzIDwtIGZEYXRhKERhdGEpW1siUUNGbGFncyJdXQ0KYGBgDQoNCkRlZmluZSBRQyB0YWJsZSBmb3IgUHJvYmUgUUMNCg0KYGBge3IgZGVmaW5lX3FjX3RhYmxlfQ0KcWNfZGYgPC0gZGF0YS5mcmFtZShQYXNzZWQgPSBzdW0ocm93U3VtcyhQcm9iZVFDUmVzdWx0c1ssIC0xXSkgPT0gMCksDQogICAgICAgICAgICAgICAgICAgIEdsb2JhbCA9IHN1bShQcm9iZVFDUmVzdWx0cyRHbG9iYWxHcnViYnNPdXRsaWVyKSwNCiAgICAgICAgICAgICAgICAgICAgTG9jYWwgPSBzdW0ocm93U3VtcyhQcm9iZVFDUmVzdWx0c1ssIC0yOi0xXSkgPiAwDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICYgIVByb2JlUUNSZXN1bHRzJEdsb2JhbEdydWJic091dGxpZXIpKQ0KYGBgDQoNClN1YnNldCBvYmplY3QgdG8gZXhjbHVkZSBhbGwgdGhhdCBkaWQgbm90IHBhc3MgUmF0aW8gJiBHbG9iYWwgdGVzdGluZw0KDQpgYGB7ciBzdWJzZXR9DQpQcm9iZVFDUGFzc2VkIDwtIA0KICBzdWJzZXQoRGF0YSwgDQogICAgICAgICBmRGF0YShEYXRhKVtbIlFDRmxhZ3MiXV1bLGMoIkxvd1Byb2JlUmF0aW8iKV0gPT0gRkFMU0UgJg0KICAgICAgICAgICBmRGF0YShEYXRhKVtbIlFDRmxhZ3MiXV1bLGMoIkdsb2JhbEdydWJic091dGxpZXIiKV0gPT0gRkFMU0UpDQoNCkRhdGEgPC0gUHJvYmVRQ1Bhc3NlZCANCmNhdCgiQWZ0ZXIgUUMgZmVhdHVyZXM6IiwgZGltKERhdGEpWzFdLCAiXG5BZnRlciBRQyBzYW1wbGVzOiIsIGRpbShEYXRhKVsyXSkNCmBgYA0KDQpDaGVjayBob3cgbWFueSB1bmlxdWUgdGFyZ2V0cyB0aGUgb2JqZWN0IGhhcw0KDQpgYGB7ciB1bmlxdWVfY2hlY2t9DQpsZW5ndGgodW5pcXVlKGZlYXR1cmVEYXRhKERhdGEpW1siVGFyZ2V0TmFtZSJdXSkpDQpgYGANCg0KQ29sbGFwc2UgdG8gdGFyZ2V0cw0KDQpgYGB7ciBjb2xsYXBzX3RhcmdldHN9DQp0YXJnZXRfRGF0YSA8LSBhZ2dyZWdhdGVDb3VudHMoRGF0YSkNCg0KZXhwcnModGFyZ2V0X0RhdGEpWzE6NSwgMToyXQ0KYGBgDQoNCkRlZmluZSBMT1EgU0QgdGhyZXNob2xkIGFuZCBtaW5pbXVtIHZhbHVlDQoNCmBgYHtyIHNldF9MU1F9DQpjdXRvZmYgPC0gMg0KbWluTE9RIDwtIDINCmBgYA0KDQojIDQuNCBMaW1pdCBvZiBRdWFudGlmaWNhdGlvbg0KDQpXZSBkZWZpbmUgYSBsaW1pdCBvZiBxdWFudGlmaWNhdGlvbiAoTE9RKSBwZXIgUk9JL0FPSSBzZWdtZW50IGJhc2VkIG9uDQp0aGUgbmVnYXRpdmUgY29udHJvbCBwcm9iZXMgdG8gZ3VpZGUgdGhlIGZpbHRlcmluZyBvZiBzZWdtZW50cyBhbmQgZ2VuZXMNCndpdGggbG93IHNpZ25hbCByZWxhdGl2ZSB0byBiYWNrZ3JvdW5kLiBUaGUgZm9ybXVsYSBmb3IgY2FsY3VsYXRpbmcgdGhlDQpMT1EgaW4gdGhlICRpXnt0aH0kIHNlZ21lbnQgYXQgJG4kIHN0YW5kYXJkIGRldmlhdGlvbnMgKCRuID0gMiQgZm9yIHRoaXMNCnN0dWR5KSBpczogJExPUV9pPWdlb21lYW4oTmVnUHJvYmVfaSkqZ2VvU0QoTmVnUHJvYmVfaSlebiQNCg0KQ2FsY3VsYXRlIExPUSBwZXIgbW9kdWxlIHRlc3RlZA0KDQpgYGB7ciBjYWxjdWxhdGVfTE9RfQ0KTE9RIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzID0gY29sbmFtZXModGFyZ2V0X0RhdGEpKQ0KZm9yKG1vZHVsZSBpbiBtb2R1bGVzKSB7DQogIHZhcnMgPC0gcGFzdGUwKGMoIk5lZ0dlb01lYW5fIiwgIk5lZ0dlb1NEXyIpLA0KICAgICAgICAgICAgICAgICBtb2R1bGUpDQogIGlmKGFsbCh2YXJzWzE6Ml0gJWluJSBjb2xuYW1lcyhwRGF0YSh0YXJnZXRfRGF0YSkpKSkgew0KICAgIExPUVssIG1vZHVsZV0gPC0NCiAgICAgIHBtYXgobWluTE9RLA0KICAgICAgICAgICBwRGF0YSh0YXJnZXRfRGF0YSlbLCB2YXJzWzFdXSAqIA0KICAgICAgICAgICAgIHBEYXRhKHRhcmdldF9EYXRhKVssIHZhcnNbMl1dIF4gY3V0b2ZmKQ0KICB9DQp9DQpwRGF0YSh0YXJnZXRfRGF0YSkkTE9RIDwtIExPUQ0KYGBgDQoNCiMgNC41IEZpbHRlcmluZw0KDQpBZnRlciBkZXRlcm1pbmluZyB0aGUgbGltaXQgb2YgcXVhbnRpZmljYXRpb24gKExPUSkgcGVyIHNlZ21lbnQsIHdlDQpyZWNvbW1lbmQgZmlsdGVyaW5nIG91dCBlaXRoZXIgc2VnbWVudHMgYW5kL29yIGdlbmVzIHdpdGggYWJub3JtYWxseSBsb3cNCnNpZ25hbC4gRmlsdGVyaW5nIGlzIGFuIGltcG9ydGFudCBzdGVwIHRvIGZvY3VzIG9uIHRoZSB0cnVlIGJpb2xvZ2ljYWwNCmRhdGEgb2YgaW50ZXJlc3QuDQoNCldlIGRldGVybWluZSB0aGUgbnVtYmVyIG9mIGdlbmVzIGRldGVjdGVkIGluIGVhY2ggc2VnbWVudCBhY3Jvc3MgdGhlDQpkYXRhc2V0Lg0KDQpgYGB7ciBmaWx0ZXJpbmd9DQpMT1FfTWF0IDwtIGMoKQ0KZm9yKG1vZHVsZSBpbiBtb2R1bGVzKSB7DQogIGluZCA8LSBmRGF0YSh0YXJnZXRfRGF0YSkkTW9kdWxlID09IG1vZHVsZQ0KICBNYXRfaSA8LSB0KGVzQXBwbHkodGFyZ2V0X0RhdGFbaW5kLCBdLCBNQVJHSU4gPSAxLA0KICAgICAgICAgICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICAgICAgICAgICAgICAgICB4ID4gTE9RWywgbW9kdWxlXQ0KICAgICAgICAgICAgICAgICAgICAgfSkpDQogIExPUV9NYXQgPC0gcmJpbmQoTE9RX01hdCwgTWF0X2kpDQp9DQojIGVuc3VyZSBvcmRlcmluZyBzaW5jZSB0aGlzIGlzIHN0b3JlZCBvdXRzaWRlIG9mIHRoZSBnZW9teFNldA0KTE9RX01hdCA8LSBMT1FfTWF0W2ZEYXRhKHRhcmdldF9EYXRhKSRUYXJnZXROYW1lLCBdDQpgYGANCg0KIyA0LjUuMSBTZWdtZW50IEdlbmUgRGV0ZWN0aW9uDQoNCldlIGZpcnN0IGZpbHRlciBvdXQgc2VnbWVudHMgd2l0aCBleGNlcHRpb25hbGx5IGxvdyBzaWduYWwuIFRoZXNlDQpzZWdtZW50cyB3aWxsIGhhdmUgYSBzbWFsbCBmcmFjdGlvbiBvZiBwYW5lbCBnZW5lcyBkZXRlY3RlZCBhYm92ZSB0aGUNCkxPUSByZWxhdGl2ZSB0byB0aGUgb3RoZXIgc2VnbWVudHMgaW4gdGhlIHN0dWR5LiBMZXQncyB2aXN1YWxpemUgdGhlDQpkaXN0cmlidXRpb24gb2Ygc2VnbWVudHMgd2l0aCByZXNwZWN0IHRvIHRoZWlyICUgZ2VuZXMgZGV0ZWN0ZWQ6DQoNClNhdmUgZGV0ZWN0aW9uIHJhdGUgaW5mb3JtYXRpb24gdG8gcGhlbm8gZGF0YQ0KDQpgYGB7ciBzYXZlX2RldGVjdGlub19yYXRlfQ0KcERhdGEodGFyZ2V0X0RhdGEpJEdlbmVzRGV0ZWN0ZWQgPC0gDQogIGNvbFN1bXMoTE9RX01hdCwgbmEucm0gPSBUUlVFKQ0KcERhdGEodGFyZ2V0X0RhdGEpJEdlbmVEZXRlY3Rpb25SYXRlIDwtDQogIHBEYXRhKHRhcmdldF9EYXRhKSRHZW5lc0RldGVjdGVkIC8gbnJvdyh0YXJnZXRfRGF0YSkNCg0KYGBgDQoNCkRldGVybWluZSBkZXRlY3Rpb24gdGhyZXNob2xkczogMSUsIDUlLCAxMCUsIDE1JSwgXD4xNSUNCg0KYGBge3IgZGV0ZXJtaW5lK3RocmVzaG9sZHN9DQpwRGF0YSh0YXJnZXRfRGF0YSkkRGV0ZWN0aW9uVGhyZXNob2xkIDwtIA0KICBjdXQocERhdGEodGFyZ2V0X0RhdGEpJEdlbmVEZXRlY3Rpb25SYXRlLA0KICAgICAgYnJlYWtzID0gYygwLCAwLjAxLCAwLjA1LCAwLjEsIDAuMTUsIDAuMiwxKSwNCiAgICAgIGxhYmVscyA9IGMoIjwxJSIsICIxLTUlIiwgIjUtMTAlIiwgIjEwLTE1JSIsICIxNS0yMCUiLCAiPjIwJSIpKQ0KDQojIHN0YWNrZWQgYmFyIHBsb3Qgb2YgZGlmZmVyZW50IGN1dCBwb2ludHMgKDElLCA1JSwgMTAlLCAxNSUpDQpnZ3Bsb3QocERhdGEodGFyZ2V0X0RhdGEpLA0KICAgICAgIGFlcyh4ID0gRGV0ZWN0aW9uVGhyZXNob2xkKSkgKw0KICBnZW9tX2JhcihhZXMoZmlsbCA9IEFOTjIpKSArDQogIGdlb21fdGV4dChzdGF0ID0gImNvdW50IiwgYWVzKGxhYmVsID0gLi5jb3VudC4uKSwgdmp1c3QgPSAtMC41KSArDQogIHRoZW1lX2J3KCkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsIDAuMSkpKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jb2xvcl9saXN0JEFOTjIpICsNCiAgbGFicyh4ID0gIkdlbmUgRGV0ZWN0aW9uIFJhdGUiLA0KICAgICAgIHkgPSAiU2VnbWVudHMsICMiLA0KICAgICAgIGZpbGwgPSAiU2VnbWVudCBUeXBlIikNCmBgYA0KDQpjdXQgcGVyY2VudCBnZW5lcyBkZXRlY3RlZCBhdCAxLCA1LCAxMCwgMTUNCg0KYGBge3IgY3V0X3RvX3BlcmNlbnR9DQprYWJsZSh0YWJsZShwRGF0YSh0YXJnZXRfRGF0YSkkRGV0ZWN0aW9uVGhyZXNob2xkLA0KICAgICAgICAgICAgcERhdGEodGFyZ2V0X0RhdGEpJEFOTjIpKQ0KDQojIHNldCB0aHJlc2hvbGQgZm9yIGRldGVjdGlvbmxldmVsDQojIGRlZmF1bHQgMC4xDQpnZW5lX2RldF90aHJlc2hvbGQgPC0gMC4wNQ0KDQp0YXJnZXRfRGF0YSA8LQ0KICB0YXJnZXRfRGF0YVssIHBEYXRhKHRhcmdldF9EYXRhKSRHZW5lRGV0ZWN0aW9uUmF0ZSA+PSBnZW5lX2RldF90aHJlc2hvbGRdDQoNCmRpbSh0YXJnZXRfRGF0YSkNCmBgYA0KDQojIDQuNiBNYW51YWwgcmVtb3ZhbCBvZiBzYW1wbGVzL2NsYXNzZXMNCg0KYGBge3IgcmVtb3ZlX3NhbXBsZXN9DQphY3RpdmVfYW9pczwtbmFtZXMoYXMuZGF0YS5mcmFtZShhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhLCBlbHQ9ICJleHBycyIpKSkNCmBgYA0KDQpyZS1Db2xsZWN0IGFubm90YXRpb25zDQoNCmBgYHtyIGNvbGxlY3RfYW5ub3RhdGlvbnN9DQojIGdhdGhlciB0aGUgZGF0YSBhbmQgcGxvdCBpbiBvcmRlcjogY2xhc3MsIHNsaWRlIG5hbWUsIHJlZ2lvbiwgc2VnbWVudA0KI2NvdW50X21hdCA8LSBkcGx5cjo6Y291bnQocERhdGEoRGF0YSksIEFOTjEsQU5OMixBTk4zLEFOTjQsc2xpZGVfbmFtZSkNCg0KdGVtcF9xYyA8LSB0ZW1wX2RmDQp0ZW1wX3FjJFFDUmVzdWx0IDwtIFFDUmVzdWx0cyRRQ1N0YXR1cw0KdGVtcF9xYyRRQ1Jlc3VsdFt0ZW1wX3FjJFFDUmVzdWx0ID09ICJXQVJOSU5HIl0gPC0gIlgiDQoNCiNjb3VudF9tYXQgPC0gZHBseXI6OmNvdW50KGEsIEFOTjEsIEFOTjIsIHNsaWRlX25hbWUsIFFDUmVzdWx0KQ0KY291bnRfbWF0IDwtIHRlbXBfcWMgJT4lIGRwbHlyOjpjb3VudCh0ZW1wX3FjW2MoYW5uX25hbWVzLCAiUUNSZXN1bHQiKV0pDQoNCnRlc3RfZ3IgPC0gZ2F0aGVyX3NldF9kYXRhKGNvdW50X21hdCwgMTooYW5uX3NpemUrMSkpDQp0ZXN0X2dyJHggPC0gZmFjdG9yKHRlc3RfZ3IkeCwgbGFiZWxzID0gYyhhbm5fbmFtZXMsICJRQ1Jlc3VsdCIpKQ0KYGBgDQoNCnJlLVBsb3QgU2Fua2V5DQoNCmBgYHtyIHBsb3Rfc2Fua2V5LCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMX0NCmdncGxvdCh0ZXN0X2dyLCBhZXMoeCwgaWQgPSBpZCwgc3BsaXQgPSB5LCB2YWx1ZSA9IG4pKSArDQogIGdlb21fcGFyYWxsZWxfc2V0cyhhZXMoZmlsbCA9IEFOTjIpLCBhbHBoYSA9IDAuNSwgYXhpcy53aWR0aCA9IDAuMSkgKw0KICBnZW9tX3BhcmFsbGVsX3NldHNfYXhlcyhheGlzLndpZHRoID0gMC4yKSArDQogIGdlb21fcGFyYWxsZWxfc2V0c19sYWJlbHMoY29sb3IgPSAid2hpdGUiLCBzaXplID0gNSkgKw0KICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE3KSArIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKSArDQogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24oMCkpICsgDQogIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gZXhwYW5zaW9uKDApKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jb2xvcl9saXN0JEFOTjIpICsNCiAgbGFicyh4ID0gIiIsIHkgPSAiIikgKw0KICBhbm5vdGF0ZShnZW9tID0gInNlZ21lbnQiLCB4ID0gMy4yNSwgeGVuZCA9IDMuMjUsIHkgPSAxMCwgDQogICAgICAgICAgIHllbmQgPSA2MCwgbHdkID0gMikgKw0KICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCB4ID0gMy4xOSwgeSA9IDI1LCBhbmdsZSA9IDkwLCBzaXplID0gNSwNCiAgICAgICAgICAgaGp1c3QgPSAwLjUsIGxhYmVsID0gIjUwIHNlZ21lbnRzIikNCmBgYA0KDQojIDQuNyBHZW5lIERldGVjdGlvbiBSYXRlDQoNCkNhbGN1bGF0ZSBkZXRlY3Rpb24gcmF0ZQ0KDQpgYGB7ciBjbGNfZGV0ZWN0aW9uX3JhdGV9DQpMT1FfTWF0IDwtIExPUV9NYXRbLCBjb2xuYW1lcyh0YXJnZXRfRGF0YSldDQpmRGF0YSh0YXJnZXRfRGF0YSkkRGV0ZWN0ZWRTZWdtZW50cyA8LSByb3dTdW1zKExPUV9NYXQsIG5hLnJtID0gVFJVRSkNCmZEYXRhKHRhcmdldF9EYXRhKSREZXRlY3Rpb25SYXRlIDwtDQogIGZEYXRhKHRhcmdldF9EYXRhKSREZXRlY3RlZFNlZ21lbnRzIC8gbnJvdyhwRGF0YSh0YXJnZXRfRGF0YSkpDQpgYGANCg0KR2VuZSBvZiBpbnRlcmVzdCBkZXRlY3Rpb24gdGFibGUNCg0KYGBge3IgZ2VuZV9vZl9pbnRlcmVzdF90YWJsZX0NCmdvaSA8LSBjKCJQRENEMSIsICJDRDI3NCIsICJJRk5HIiwgIkNEOEEiLCAiQ0Q2OCIsICJFUENBTSIsDQogICAgICAgICAiS1JUMTgiLCAiTlBIUzEiLCAiTlBIUzIiLCAiQ0FMQjEiLCAiQ0xETjgiKQ0KZ29pX2RmIDwtIGRhdGEuZnJhbWUoDQogIEdlbmUgPSBnb2ksDQogIE51bWJlciA9IGZEYXRhKHRhcmdldF9EYXRhKVtnb2ksICJEZXRlY3RlZFNlZ21lbnRzIl0sDQogIERldGVjdGlvblJhdGUgPSBwZXJjZW50KGZEYXRhKHRhcmdldF9EYXRhKVtnb2ksICJEZXRlY3Rpb25SYXRlIl0pKQ0KYGBgDQoNCiMgNC44IEdlbmUgRmlsdGVyaW5nDQoNCldlIHdpbGwgZ3JhcGggdGhlIHRvdGFsIG51bWJlciBvZiBnZW5lcyBkZXRlY3RlZCBpbiBkaWZmZXJlbnQNCnBlcmNlbnRhZ2VzIG9mIHNlZ21lbnRzLiBCYXNlZCBvbiB0aGUgdmlzdWFsaXphdGlvbiBiZWxvdywgd2UgY2FuIGJldHRlcg0KdW5kZXJzdGFuZCBnbG9iYWwgZ2VuZSBkZXRlY3Rpb24gaW4gb3VyIHN0dWR5IGFuZCBzZWxlY3QgaG93IG1hbnkgbG93DQpkZXRlY3RlZCBnZW5lcyB0byBmaWx0ZXIgb3V0IG9mIHRoZSBkYXRhc2V0LiBHZW5lIGZpbHRlcmluZyBpbmNyZWFzZXMNCnBlcmZvcm1hbmNlIG9mIGRvd25zdHJlYW0gc3RhdGlzdGljYWwgdGVzdHMgYW5kIGltcHJvdmVzIGludGVycHJldGF0aW9uDQpvZiB0cnVlIGJpb2xvZ2ljYWwgc2lnbmFsLg0KDQpQbG90IGRldGVjdGlvbiByYXRlDQoNCmBgYHtyIHBsb3RfZGV0X3JhdGV9DQpwbG90X2RldGVjdCA8LSBkYXRhLmZyYW1lKEZyZXEgPSBjKDEsIDUsIDEwLCAyMCwgMzAsIDUwKSkNCnBsb3RfZGV0ZWN0JE51bWJlciA8LQ0KICB1bmxpc3QobGFwcGx5KGMoMC4wMSwgMC4wNSwgMC4xLCAwLjIsIDAuMywgMC41KSwNCiAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7c3VtKGZEYXRhKHRhcmdldF9EYXRhKSREZXRlY3Rpb25SYXRlID49IHgpfSkpDQpwbG90X2RldGVjdCRSYXRlIDwtIHBsb3RfZGV0ZWN0JE51bWJlciAvIG5yb3coZkRhdGEodGFyZ2V0X0RhdGEpKQ0Kcm93bmFtZXMocGxvdF9kZXRlY3QpIDwtIHBsb3RfZGV0ZWN0JEZyZXENCg0KZ2dwbG90KHBsb3RfZGV0ZWN0LCBhZXMoeCA9IGFzLmZhY3RvcihGcmVxKSwgeSA9IFJhdGUsIGZpbGwgPSBSYXRlKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gZm9ybWF0QyhOdW1iZXIsIGZvcm1hdCA9ICJkIiwgYmlnLm1hcmsgPSAiLCIpKSwNCiAgICAgICAgICAgIHZqdXN0ID0gMS42LCBjb2xvciA9ICJibGFjayIsIHNpemUgPSA0KSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICJvcmFuZ2UyIiwgbWlkID0gImxpZ2h0Ymx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgIGhpZ2ggPSAiZG9kZ2VyYmx1ZTMiLCBtaWRwb2ludCA9IDAuNjUsDQogICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCwxKSwNCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArDQogIHRoZW1lX2J3KCkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50LCBsaW1pdHMgPSBjKDAsMSksDQogICAgICAgICAgICAgICAgICAgICBleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMCkpKSArDQogIGxhYnMoeCA9ICIlIG9mIFNlZ21lbnRzIiwNCiAgICAgICB5ID0gIkdlbmVzIERldGVjdGVkLCAlIG9mIFBhbmVsID4gTE9RIikNCmBgYA0KDQpTdWJzZXQgdG8gdGFyZ2V0IGdlbmVzIGRldGVjdGVkIGluIGF0IGxlYXN0IDEwJSBvZiB0aGUgc2FtcGxlcy4gQWxzbw0KbWFudWFsbHkgaW5jbHVkZSB0aGUgbmVnYXRpdmUgY29udHJvbCBwcm9iZSwgZm9yIGRvd25zdHJlYW0gdXNlDQoNCmBgYHtyIHN1YnNldF90b18xMHBfZGV0ZWN0ZWRfZ2VuZXN9DQojIGRlZmF1bHQ9MC4xDQpuZWdhdGl2ZVByb2JlZkRhdGEgPC0gc3Vic2V0KGZEYXRhKHRhcmdldF9EYXRhKSwgQ29kZUNsYXNzID09ICJOZWdhdGl2ZSIpDQpuZWdfcHJvYmVzIDwtIHVuaXF1ZShuZWdhdGl2ZVByb2JlZkRhdGEkVGFyZ2V0TmFtZSkNCnRhcmdldF9EYXRhIDwtIA0KICB0YXJnZXRfRGF0YVtmRGF0YSh0YXJnZXRfRGF0YSkkRGV0ZWN0aW9uUmF0ZSA+PSAwLjA1IHwNCiAgICAgICAgICAgICAgICAgICAgZkRhdGEodGFyZ2V0X0RhdGEpJFRhcmdldE5hbWUgJWluJSBuZWdfcHJvYmVzLCBdDQoNCiMgcmV0YWluIG9ubHkgZGV0ZWN0ZWQgZ2VuZXMgb2YgaW50ZXJlc3QNCmdvaSA8LSBnb2lbZ29pICVpbiUgcm93bmFtZXModGFyZ2V0X0RhdGEpXQ0KDQpkaW0odGFyZ2V0X0RhdGEpDQpgYGANCg0KIyA1IE5vcm1hbGl6YXRpb24NCg0KV2Ugd2lsbCBub3cgbm9ybWFsaXplIHRoZSBHZW9NeCBkYXRhIGZvciBkb3duc3RyZWFtIHZpc3VhbGl6YXRpb25zIGFuZA0KZGlmZmVyZW50aWFsIGV4cHJlc3Npb24uIFRoZSB0d28gY29tbW9uIG1ldGhvZHMgZm9yIG5vcm1hbGl6YXRpb24gb2YNCkRTUC1OR1MgUk5BIGRhdGEgYXJlIGkpIHF1YXJ0aWxlIDMgKFEzKSBvciBpaSkgYmFja2dyb3VuZCBub3JtYWxpemF0aW9uLg0KDQpCb3RoIG9mIHRoZXNlIG5vcm1hbGl6YXRpb24gbWV0aG9kcyBlc3RpbWF0ZSBhIG5vcm1hbGl6YXRpb24gZmFjdG9yIHBlcg0Kc2VnbWVudCB0byBicmluZyB0aGUgc2VnbWVudCBkYXRhIGRpc3RyaWJ1dGlvbnMgdG9nZXRoZXIuIE1vcmUgYWR2YW5jZWQNCm1ldGhvZHMgZm9yIG5vcm1hbGl6YXRpb24gYW5kIG1vZGVsaW5nIGFyZSB1bmRlciBhY3RpdmUgZGV2ZWxvcG1lbnQuDQpIb3dldmVyLCBmb3IgbW9zdCBzdHVkaWVzLCB0aGVzZSBtZXRob2RzIGFyZSBzdWZmaWNpZW50IGZvcg0KdW5kZXJzdGFuZGluZyBkaWZmZXJlbmNlcyBiZXR3ZWVuIGJpb2xvZ2ljYWwgY2xhc3NlcyBvZiBzZWdtZW50cyBhbmQNCnNhbXBsZXMuDQoNClEzIG5vcm1hbGl6YXRpb24gaXMgdHlwaWNhbGx5IHRoZSBwcmVmZXJyZWQgbm9ybWFsaXphdGlvbiBzdHJhdGVneSBmb3INCm1vc3QgRFNQLU5HUyBSTkEgc3R1ZGllcy4gR2l2ZW4gdGhlIGxvdyBuZWdhdGl2ZSBwcm9iZSBjb3VudHMgaW4gdGhpcw0KcGFydGljdWxhciBkYXRhc2V0IGFzIHNob3duIGR1cmluZyBTZWdtZW50IFFDLCB3ZSB3b3VsZCBmdXJ0aGVyIGF2b2lkDQpiYWNrZ3JvdW5kIG5vcm1hbGl6YXRpb24gYXMgaXQgbWF5IGJlIGxlc3Mgc3RhYmxlLg0KDQpCZWZvcmUgbm9ybWFsaXphdGlvbiwgd2Ugd2lsbCBleHBsb3JlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdXBwZXINCnF1YXJ0aWxlIChRMykgb2YgdGhlIGNvdW50cyBpbiBlYWNoIHNlZ21lbnQgd2l0aCB0aGUgZ2VvbWV0cmljIG1lYW4gb2YNCnRoZSBuZWdhdGl2ZSBjb250cm9sIHByb2JlcyBpbiB0aGUgZGF0YS4gSWRlYWxseSwgdGhlcmUgc2hvdWxkIGJlIGENCnNlcGFyYXRpb24gYmV0d2VlbiB0aGVzZSB0d28gdmFsdWVzIHRvIGVuc3VyZSB3ZSBoYXZlIHN0YWJsZSBtZWFzdXJlIG9mDQpRMyBzaWduYWwuIElmIHlvdSBkbyBub3Qgc2VlIHN1ZmZpY2llbnQgc2VwYXJhdGlvbiBiZXR3ZWVuIHRoZXNlIHZhbHVlcywNCnlvdSBtYXkgY29uc2lkZXIgbW9yZSBhZ2dyZXNzaXZlIGZpbHRlcmluZyBvZiBsb3cgc2lnbmFsIHNlZ21lbnRzL2dlbmVzLg0KDQpHcmFwaCBRMyB2YWx1ZSB2cyBuZWdHZW9NZWFuIG9mIE5lZ2F0aXZlcw0KDQpgYGB7ciBscG90X3EzX25lZ0dlb01lYW59DQphbm5fb2ZfaW50ZXJlc3QgPC0gIkFOTjIiDQpTdGF0X2RhdGEgPC0gDQogIGRhdGEuZnJhbWUocm93Lm5hbWVzID0gY29sbmFtZXMoZXhwcnModGFyZ2V0X0RhdGEpKSwNCiAgICAgICAgICAgICBTZWdtZW50ID0gY29sbmFtZXMoZXhwcnModGFyZ2V0X0RhdGEpKSwNCiAgICAgICAgICAgICBBbm5vdGF0aW9uID0gcERhdGEodGFyZ2V0X0RhdGEpWywgYW5uX29mX2ludGVyZXN0XSwNCiAgICAgICAgICAgICBRMyA9IHVubGlzdChhcHBseShleHBycyh0YXJnZXRfRGF0YSksIDIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGUsIDAuNzUsIG5hLnJtID0gVFJVRSkpLA0KICAgICAgICAgICAgIE5lZ1Byb2JlID0gZXhwcnModGFyZ2V0X0RhdGEpW25lZ19wcm9iZXMsIF0pDQpTdGF0X2RhdGFfbSA8LSBtZWx0KFN0YXRfZGF0YSwgbWVhc3VyZS52YXJzID0gYygiUTMiLCAiTmVnUHJvYmUiKSwNCiAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUubmFtZSA9ICJTdGF0aXN0aWMiLCB2YWx1ZS5uYW1lID0gIlZhbHVlIikNCg0KcGx0MSA8LSBnZ3Bsb3QoU3RhdF9kYXRhX20sDQogICAgICAgICAgICAgICBhZXMoeCA9IFZhbHVlLCBmaWxsID0gU3RhdGlzdGljKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNDApICsgdGhlbWVfYncoKSArDQogIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cyIikgKw0KICBmYWNldF93cmFwKH5Bbm5vdGF0aW9uLCBucm93ID0gMSkgKyANCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9IDMsIHR5cGUgPSAicXVhbCIpICsNCiAgbGFicyh4ID0gIkNvdW50cyIsIHkgPSAiU2VnbWVudHMsICMiKQ0KDQpwbHQyIDwtIGdncGxvdChTdGF0X2RhdGEsDQogICAgICAgICAgICAgICBhZXMoeCA9IE5lZ1Byb2JlLCB5ID0gUTMsIGNvbG9yID0gQW5ub3RhdGlvbikpICsNCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBsdHkgPSAiZGFzaGVkIiwgY29sb3IgPSAiZGFya2dyYXkiKSArDQogIGdlb21fcG9pbnQoKSArIGd1aWRlcyhjb2xvciA9ICJub25lIikgKyB0aGVtZV9idygpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMiIpICsNCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKw0KICBsYWJzKHggPSAiTmVnYXRpdmUgUHJvYmUgR2VvTWVhbiwgQ291bnRzIiwgeSA9ICJRMyBWYWx1ZSwgQ291bnRzIikNCg0KcGx0MyA8LSBnZ3Bsb3QoU3RhdF9kYXRhLA0KICAgICAgICAgICAgICAgYWVzKHggPSBOZWdQcm9iZSwgeSA9IFEzIC8gTmVnUHJvYmUsIGNvbG9yID0gQW5ub3RhdGlvbikpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMSwgbHR5ID0gImRhc2hlZCIsIGNvbG9yID0gImRhcmtncmF5IikgKw0KICBnZW9tX3BvaW50KCkgKyB0aGVtZV9idygpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMiIpICsNCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKw0KICBsYWJzKHggPSAiTmVnYXRpdmUgUHJvYmUgR2VvTWVhbiwgQ291bnRzIiwgeSA9ICJRMy9OZWdQcm9iZSBWYWx1ZSwgQ291bnRzIikNCg0KYnRtX3JvdyA8LSBwbG90X2dyaWQocGx0MiwgcGx0MywgbnJvdyA9IDEsIGxhYmVscyA9IGMoIkIiLCAiIiksDQogICAgICAgICAgICAgICAgICAgICByZWxfd2lkdGhzID0gYygwLjQzLDAuNTcpKQ0KcGxvdF9ncmlkKHBsdDEsIGJ0bV9yb3csIG5jb2wgPSAxLCBsYWJlbHMgPSBjKCJBIiwgIiIpKQ0KDQpgYGANCg0KUTMgbm9ybSAoNzV0aCBwZXJjZW50aWxlKSBmb3IgV1RBL0NUQSB3aXRoIG9yIHdpdGhvdXQgY3VzdG9tIHNwaWtlLWlucw0KDQpgYGB7ciBxM19ub3JtfQ0KdGFyZ2V0X0RhdGEgPC0gbm9ybWFsaXplKHRhcmdldF9EYXRhICwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9tZXRob2QgPSAicXVhbnQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzaXJlZFF1YW50aWxlID0gLjc1LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b0VsdCA9ICJxX25vcm0iKQ0KIywgZGF0YV90eXBlID0gIlJOQSIgZGVwcmljYXRlZCBhZnRlciA0LjENCg0KYGBgDQoNClF1YW50aWxlIE5vcm1hbGl6YXRpb24NCg0KYGBge3IgcXVhbnRpbGV9DQpxdWFudGlsZSA8LSBub3JtYWxpemUucXVhbnRpbGVzKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dKQ0Kcm93bmFtZXMocXVhbnRpbGUpIDwtIHJvd25hbWVzKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dKQ0KY29sbmFtZXMocXVhbnRpbGUpIDwtIGNvbG5hbWVzKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dKQ0KDQphc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAicXVhbnRpbGVfbm9ybSIpIDwtICBxdWFudGlsZQ0KYGBgDQoNCkJhY2tncm91bmQgbm9ybWFsaXphdGlvbiBmb3IgV1RBL0NUQSB3aXRob3V0IGN1c3RvbSBzcGlrZS1pbg0KDQpgYGB7ciBiZ19ub3JtfQ0KdGFyZ2V0X0RhdGEgPC0gbm9ybWFsaXplKHRhcmdldF9EYXRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX21ldGhvZCA9ICJuZWciLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJvbUVsdCA9ICJleHBycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvRWx0ID0gIm5lZ19ub3JtIikNCg0KIyAsIGRhdGFfdHlwZSA9ICJSTkEiIGRlcHJpY2F0ZWQgYWZ0ZXIgNC4xDQpgYGANCg0KIyA1LjEgVmlzdWFsaXplIHRoZSBmaXJzdCAxMCBzZWdtZW50cyB3aXRoIGVhY2ggbm9ybWFsaXphdGlvbiBtZXRob2Qgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KVXNlIHRoZSB0YWItbWVudSB0byBuYXZpZ2F0ZSENCg0KYGBge3IgdmlzdWxhaXplX25vcm1zfQ0KDQojRml4IHplcm8gdmFsdWVzLCB3aGljaCBnbyB0byAtaW5mIGluIGxvZyB0cmFuc2Zvcm0gaW4gc3RhbmRhcmQgYm94cGxvdA0KIyB0ZW1wIDwtYXMubWF0cml4KGV4cHJzKCh0YXJnZXRfRGF0YSlbLDE6MTBdKSkNCiMgbG9uZyA8LSBtZWx0KHRlbXApDQojIGNvbG5hbWVzKGxvbmcpIDwtIGMoImdlbmUiLCJzZWdtZW50IiwiY291bnQiKQ0KIyBnZ3Bsb3QobG9uZywgYWVzKHg9c2VnbWVudCx5PWNvdW50KSkgKw0KIyAgICAgZ2VvbV9ib3hwbG90KGZpbGw9IiM5RURBRTUiKSArDQojICAgICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9c2NhbGVzOjpwc2V1ZG9fbG9nX3RyYW5zKGJhc2UgPSAxMCkpICsNCiMgICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoMToxMCkpICsNCiMgICAgIGxhYnModGl0bGU9IlJhdyBjb3VudHMiLCB4PSJzZWdtZW50IiwgeSA9ICJDb3VudHMsIFJhdyIpDQojIA0KIyANCiMgdGVtcCA8LWFzLm1hdHJpeChhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhWywxOjEwXSwgZWx0ID0gInFfbm9ybSIpKQ0KIyBsb25nIDwtIG1lbHQodGVtcCkNCiMgY29sbmFtZXMobG9uZykgPC0gYygiZ2VuZSIsInNlZ21lbnQiLCJjb3VudCIpDQojIGdncGxvdChsb25nLCBhZXMoeD1zZWdtZW50LHk9Y291bnQpKSArDQojICAgICBnZW9tX2JveHBsb3QoZmlsbCA9ICIjMkNBMDJDIikgKw0KIyAgICAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPXNjYWxlczo6cHNldWRvX2xvZ190cmFucyhiYXNlID0gMTApKSArDQojICAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1jKDE6MTApKSArDQojICAgICBsYWJzKHRpdGxlPSJRMyBOb3JtIENvdW50cyIsIHg9InNlZ21lbnQiLCB5ID0gIkNvdW50cywgUTMgTm9ybWFsaXplZCIpDQojIA0KIyANCiMgdGVtcCA8LWFzLm1hdHJpeChhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhWywxOjEwXSwgZWx0ID0gIm5lZ19ub3JtIikpDQojIGxvbmcgPC0gbWVsdCh0ZW1wKQ0KIyBjb2xuYW1lcyhsb25nKSA8LSBjKCJnZW5lIiwic2VnbWVudCIsImNvdW50IikNCiMgZ2dwbG90KGxvbmcsIGFlcyh4PXNlZ21lbnQseT1jb3VudCkpICsNCiMgICAgIGdlb21fYm94cGxvdChmaWxsID0gIiNGRjdGMEUiKSArDQojICAgICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9c2NhbGVzOjpwc2V1ZG9fbG9nX3RyYW5zKGJhc2UgPSAxMCkpICsNCiMgICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoMToxMCkpICsNCiMgICAgIGxhYnModGl0bGU9Ik5lZyBOb3JtIENvdW50cyIsIHg9InNlZ21lbnQiLCB5ID0gIkNvdW50cywgTmVnLiBOb3JtYWxpemVkIikNCmBgYA0KDQojIyByYXcgY291bnRzDQoNCmBgYHtyfQ0KYm94cGxvdChleHBycyh0YXJnZXRfRGF0YSlbLDE6OF0sDQogICAgICAgIGNvbCA9ICIjOUVEQUU1IiwgbWFpbiA9ICJSYXcgQ291bnRzIiwNCiAgICAgICAgbG9nID0gInkiLCBuYW1lcyA9IDE6OCwgeGxhYiA9ICJTZWdtZW50IiwNCiAgICAgICAgeWxhYiA9ICJDb3VudHMsIFJhdyIpDQpgYGANCg0KIyMgUTMgbm9ybWFsaXplZCB7LmFjdGl2ZX0NCg0KYGBge3J9DQpib3hwbG90KGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGFbLDE6OF0sIGVsdCA9ICJxX25vcm0iKSwNCiAgICAgICAgY29sID0gIiMyQ0EwMkMiLCBtYWluID0gIlEzIE5vcm0gQ291bnRzIiwNCiAgICAgICAgbG9nID0gInkiLCBuYW1lcyA9IDE6OCwgeGxhYiA9ICJTZWdtZW50IiwNCiAgICAgICAgeWxhYiA9ICJDb3VudHMsIFEzIE5vcm1hbGl6ZWQiKQ0KYGBgDQoNCiMjIFF1YW50aWxlIG5vcm1hbGl6ZWQgKHRlc3RpbmcgcGhhc2UpDQoNCmBgYHtyfQ0KYm94cGxvdChhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhWywxOjhdLCBlbHQgPSAicXVhbnRpbGVfbm9ybSIpLA0KICAgICAgICBjb2wgPSAicGluayIsIG1haW4gPSAiUXVhbnRpbGUgTm9ybSBDb3VudHMiLA0KICAgICAgICBsb2cgPSAieSIsIG5hbWVzID0gMTo4LCB4bGFiID0gIlNlZ21lbnQiLA0KICAgICAgICB5bGFiID0gIkNvdW50cywgUXVhbnRpbGUgTm9ybWFsaXplZCIpDQpgYGANCg0KIyMgTmVnYXRpdmUgcHJvYmUgbm9ybWFsaXphdGlvbg0KDQpgYGB7cn0NCmJveHBsb3QoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVssMTo4XSwgZWx0ID0gIm5lZ19ub3JtIiksDQogICAgICAgIGNvbCA9ICIjRkY3RjBFIiwgbWFpbiA9ICJOZWcgTm9ybSBDb3VudHMiLA0KICAgICAgICBsb2cgPSAieSIsIG5hbWVzID0gMTo4LCB4bGFiID0gIlNlZ21lbnQiLA0KICAgICAgICB5bGFiID0gIkNvdW50cywgTmVnLiBOb3JtYWxpemVkIikNCg0KYGBgDQoNCiMgNiBVbnN1cGVydmlzZWQgQW5hbHlzaXMNCg0KIyA2LjEgVU1BUCB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpVc2UgdGhlIHRhYi1tZW51IHRvIG5hdmlnYXRlIQ0KDQojIyAxDQoNCmBgYHtyIHVtYXAsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTh9DQpjdXN0b21fdW1hcCA8LSB1bWFwOjp1bWFwLmRlZmF1bHRzDQpjdXN0b21fdW1hcCRyYW5kb21fc3RhdGUgPC0gNDINCiMgcnVuIFVNQVANCg0KdW1hcF9vdXQgPC0NCiAgdW1hcCh0KGxvZzIoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSAsIGVsdCA9ICJxX25vcm0iKSkpLA0KICAgICAgIGNvbmZpZyA9IGN1c3RvbV91bWFwKQ0KcERhdGEodGFyZ2V0X0RhdGEpWywgYygiVU1BUDEiLCAiVU1BUDIiKV0gPC0gdW1hcF9vdXQkbGF5b3V0WywgYygxLDIpXQ0KZ2dwbG90KHBEYXRhKHRhcmdldF9EYXRhKSwNCiAgICAgICBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gc2xpZGVfbmFtZSwgc2hhcGUgPSBBTk4xKSkgKw0KDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsNCiAgI2dlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9cm93Lm5hbWVzKHBEYXRhKHRhcmdldF9EYXRhKSkpLCBzaXplPTIsbWF4Lm92ZXJsYXBzID0gMTAwKSsNCiAgdGhlbWVfYncoKQ0KDQpiYXRjaF9jb3JyZWN0aW9uX25lZWRlZCA9IEZBTFNFDQpgYGANCg0KIyMgMg0KDQpgYGB7ciB1bWFwMiwgZmlnLndpZHRoPTEwLGZpZy5oZWlnaHQ9OH0NCmdncGxvdChwRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyLCBjb2xvciA9IEFOTjMsIHNoYXBlID0gQU5OMSkpICsNCg0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArDQogICNnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsPXJvdy5uYW1lcyhwRGF0YSh0YXJnZXRfRGF0YSkpKSwgc2l6ZT0yLG1heC5vdmVybGFwcyA9IDEwMCkrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQojIDYuMiBCYXRjaCBjb3JyZWN0aW9uIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNCklmIHRoZSBkYXRhIGlzIG9ic2VydmVkIHRvIGhhdmUgYSBiYXRjaCBlZmZlY3QgaXQgY2FuIGJlIGNvcnJlY3RlZCB3aXRoIHRoZSBtZXRob2RzOiBSVVY0LCBMSU1NQSwgb3IgQ29tYmF0LVNlcS4gRWFjaCBtZXRob2QgaXMgZG9uZSBhbmQgdGhlIGJlc3Qgb25lIGlzIHBpY2tlZCBhbmQgdXNlZC4NCg0KVGhlIHN0YW5kUiBwYWNrYWdlIGlzIHNob3J0IGZvciBTcGF0aWFsIHRyYW5zY3JpcHRvbWljcyBhbmFseXplcyBhbmQgZGVjb2RpbmcgaW4gUiwgaXQgYWltcyBhdCBwcm92aWRpbmcgZ29vZCBwcmFjdGljZSBwaXBlbGluZSBhbmQgdXNlZnVsIGZ1bmN0aW9ucyBmb3IgdXNlcnMgdG8gYW5hbHl6ZSBOYW5vc3RyaW5n4oCZcyBHZW9NeCBEU1AgZGF0YS4gSW4gdGhlIE5hbm9zdHJpbmfigJlzIEdlb01YIERTUCBwcm90b2NvbCwgZHVlIHRvIHRoZSBmYWN0IHRoYXQgb25lIHNsaWRlIGlzIG9ubHkgYmlnIGVub3VnaCBmb3IgYSBoYW5kZnVsIG9mIHRpc3N1ZSBzZWdtZW50cyAoUk9JcyksIGl0IGlzIGNvbW1vbiB0aGF0IHdlIHNlZSB0aGUgRFNQIGRhdGEgYmVpbmcgY29uZm91bmRlZCBieSB0aGUgYmF0Y2ggZWZmZWN0IGludHJvZHVjZWQgYnkgZGlmZmVyZW50IHNsaWRlcy4gSW4gb3JkZXIgdG8gZXN0YWJsaXNoIGZhaXIgY29tcGFyaXNvbiBiZXR3ZWVuIFJPSXMgbGF0ZXIgb24sIGl0IGlzIG5lY2Vzc2FyeSB0byByZW1vdmUgdGhpcyBiYXRjaCBlZmZlY3QgZnJvbSB0aGUgZGF0YS4gKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL3N0YW5kUi9pbnN0L2RvYy9zdGFuZFJfaW50cm9kdWN0aW9uLmh0bWwpDQoNCkZvciBSVVY0IGNvcnJlY3Rpb24sIHRoZSBmdW5jdGlvbiBpcyByZXF1aXJpbmcgMyBwYXJhbWV0ZXJzIG90aGVyIHRoYW4gdGhlIGlucHV0IG9iamVjdCwgaW5jbHVkaW5nIGZhY3RvcnM6IHRoZSBmYWN0b3Igb2YgaW50ZXJlc3QsIGkuZS4gdGhlIGJpb2xvZ2ljYWwgdmFyaWF0aW9uIHdlIHBsYW4gdG8ga2VlcDsgTkNHczogdGhlIGxpc3Qgb2YgbmVnYXRpdmUgY29udHJvbCBnZW5lcyBkZXRlY3RlZCB1c2luZyB0aGUgZnVuY3Rpb24gZmluZE5DR3M7IGFuZCBrOiBpcyB0aGUgbnVtYmVyIG9mIHVud2FudGVkIGZhY3RvcnMgdG8gdXNlLCBpbiB0aGUgUlVWIGRvY3VtZW50YXRpb24sIGl0IGlzIHN1Z2dlc3QgdGhhdCB3ZSBzaG91bGQgdXNlIHRoZSBzbWFsbGVzdCBrIG9uY2Ugd2UgZG9u4oCZdCBvYnNlcnZlIHRlY2huaWNhbCB2YXJpYXRpb24gaW4gdGhlIGRhdGEuDQoNCkFub3RoZXIgb3B0aW9uIGlzIHNldCB0aGUgcGFyYW1ldGVyIG1ldGhvZCB0byDigJxMaW1tYeKAnSwgd2hpY2ggdXNlcyB0aGUgcmVtb3ZlIGJhdGNoIGNvcnJlY3Rpb24gbWV0aG9kIGZyb20gbGltbWEuIEluIHRoaXMgbW9kZSwgdGhlIGZ1bmN0aW9uIGlzIHJlcXVpcmluZyAyIHBhcmFtZXRlcnMsIGluY2x1ZGluZyBiYXRjaDogYSB2ZWN0b3IgdGhhdCBpbmRpY2F0aW5nIGJhdGNoZXMgZm9yIGFsbCBzYW1wbGVzOyBhbmQgZGVzaWduOiBhIGRlc2lnbiBtYXRyaXggd2hpY2ggaXMgZ2VuZXJhdGVkIGJ5IG1vZGVsLm1hdHJpeCwgaW4gdGhlIGRlc2lnbiBtYXRyaXgsIGFsbCBiaW9sb2dpY2FsbHktcmVsZXZhbnQgZmFjdG9ycyBzaG91bGQgYmUgaW5jbHVkZWQuDQoNCkNvbUJhdC1zZXEgaXMgYSBiYXRjaCBlZmZlY3QgYWRqdXN0bWVudCB0b29sIGZvciBidWxrIFJOQS1zZXEgY291bnQgZGF0YS4gSXQgaXMgYW4gaW1wcm92ZWQgbW9kZWwgYmFzZWQgb24gdGhlIHBvcHVsYXIgQ29tQmF0LCB0byBhZGRyZXNzIGl0cyBsaW1pdGF0aW9ucyB0aHJvdWdoIG5vdmVsIG1ldGhvZHMgZGVzaWduZWQgc3BlY2lmaWNhbGx5IGZvciBSTkEtU2VxIHN0dWRpZXMuIENvbUJhdC1zZXEgdGFrZXMgdW50cmFuc2Zvcm1lZCwgcmF3IGNvdW50IG1hdHJpeCBhcyBpbnB1dC4gU2FtZSBhcyBDb21CYXQsIGl0IHJlcXVpcmVzIGEga25vd24gYmF0Y2ggdmFyaWFibGUuIChodHRwczovL2dpdGh1Yi5jb20vemhhbmd5dXFpbmcvQ29tQmF0LXNlcSkNCg0KIyMgU2V0IHVwIHN0YW5kUiBvYmplY3QNCg0KYGBge3IgYmF0Y2ggY29ycmVjdGlvbiwgZXZhbD1iYXRjaF9jb3JyZWN0aW9uX25lZWRlZH0NCmNvdW50X2dlb214IDwtIGFzLmRhdGEuZnJhbWUodGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1siZXhwcnMiXV0pDQoNCnNhbXBsZV9nZW9teCA8LSB0YXJnZXRfRGF0YUBwaGVub0RhdGFAZGF0YQ0Kc2FtcGxlX2dlb214JHJvaSA8LSB0YXJnZXRfRGF0YUBwcm90b2NvbERhdGFAZGF0YSRyb2kNCnNhbXBsZV9nZW9teCA8LSBhcy5kYXRhLmZyYW1lKHNhbXBsZV9nZW9teCkNCnNhbXBsZV9nZW9teCRTYW1wbGVfSUQgPC0gcm93bmFtZXMoc2FtcGxlX2dlb214KQ0Kc2FtcGxlX2dlb214JFNlZ21lbnREaXNwbGF5TmFtZSA8LSBwYXN0ZShzYW1wbGVfZ2VvbXgkYHNjYW4gbmFtZWAsIHNhbXBsZV9nZW9teCRyb2ksIHNhbXBsZV9nZW9teCRzZWdtZW50LCBzZXAgPSAiIHwgIikNCnNhbXBsZV9nZW9teCRST0lDb29yZGluYXRlWCA8LSAxDQpzYW1wbGVfZ2VvbXgkUk9JQ29vcmRpbmF0ZVkgPC0gMQ0KDQoNCmZlYXR1cmVfZ2VvbXggPC0gZkRhdGEoRGF0YSkNCmZlYXR1cmVfZ2VvbXggPC0gZmVhdHVyZV9nZW9teFtjKCJSVFNfSUQiLCAiVGFyZ2V0TmFtZSIsICJQcm9iZUlEIiwgIk5lZ2F0aXZlIildDQpmZWF0dXJlX2dlb214IDwtIGFzLmRhdGEuZnJhbWUoZmVhdHVyZV9nZW9teCkNCnJvd25hbWVzKGZlYXR1cmVfZ2VvbXgpIDwtIE5VTEwNCg0KbWF0Y2hpbmcgPC0gc2FtcGxlX2dlb214JFNlZ21lbnREaXNwbGF5TmFtZVtzYW1wbGVfZ2VvbXgkU2FtcGxlX0lEICVpbiUgY29sbmFtZXMoY291bnRfZ2VvbXgpXQ0KY29sbmFtZXMoY291bnRfZ2VvbXgpIDwtIG1hdGNoaW5nDQpjb3VudF9nZW9teCRUYXJnZXROYW1lIDwtIHJvd25hbWVzKGNvdW50X2dlb214KQ0Kcm93bmFtZXMoY291bnRfZ2VvbXgpIDwtIE5VTEwNCg0Kc3BlIDwtIHJlYWRHZW9NeChjb3VudF9nZW9teCwgc2FtcGxlX2dlb214LCBmZWF0dXJlQW5ub0ZpbGUgPSBmZWF0dXJlX2dlb214LCBoYXNOZWdQcm9iZSA9IFRSVUUpDQoNCmNvbERhdGEoc3BlKSRyZWdpb25zIDwtIHBhc3RlMChjb2xEYXRhKHNwZSkkQU5OMiwiXyIsY29sRGF0YShzcGUpJEFOTjEpIHw+IA0KICAoXCguKSBnc3ViKCJfR2VvbWV0cmljIFNlZ21lbnQiLCIiLC4pKSgpIHw+DQogIHBhc3RlMCgiXyIsY29sRGF0YShzcGUpJHBhdGhvbG9neSkgfD4NCiAgKFwoLikgZ3N1YigiX05BIiwiX25zIiwuKSkoKQ0KDQpjb2xEYXRhKHNwZSkkcmVnaW9ucyA8LSBwYXN0ZTAoY29sRGF0YShzcGUpJEFOTjIsIl8iLGNvbERhdGEoc3BlKSRBTk4xKSB8PiANCiAgKFwoLikgZ3N1YigiX0dlb21ldHJpYyBTZWdtZW50IiwiIiwuKSkoKSB8Pg0KICBwYXN0ZTAoIl8iLGNvbERhdGEoc3BlKSRwYXRob2xvZ3kpIHw+DQogIChcKC4pIGdzdWIoIl9OQSIsIl9ucyIsLikpKCkNCmNvbERhdGEoc3BlKSRiaW9sb2d5IDwtIHBhc3RlMChjb2xEYXRhKHNwZSkkcmVnaW9ucykNCmBgYA0KDQojIyBTZWUgb3B0aW1hbCBrIHZhbHVlIGZvciBSVVY0DQoNCmBgYHtyLCBldmFsPWJhdGNoX2NvcnJlY3Rpb25fbmVlZGVkfQ0Kc3BlIDwtIGZpbmROQ0dzKHNwZSwgYmF0Y2hfbmFtZSA9ICJzbGlkZV9uYW1lIiwgdG9wX24gPSA1MDApDQpmaW5kQmVzdEsoc3BlLCBtYXhLID0gMTAsIGZhY3Rvcl9vZl9pbnQgPSAiYmlvbG9neSIsIE5DR3MgPSBtZXRhZGF0YShzcGUpJE5DR3MsIGZhY3Rvcl9iYXRjaCA9ICJzbGlkZV9uYW1lIikNCmBgYA0KDQojIyBSVVY0DQoNCmBgYHtyIHJ1djQgYmF0Y2gsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTEwLCBldmFsPWJhdGNoX2NvcnJlY3Rpb25fbmVlZGVkfQ0KcnV2NCA8LSBnZW9teEJhdGNoQ29ycmVjdGlvbihzcGUsIGZhY3RvcnMgPSAiYmlvbG9neSIsIA0KICAgICAgICAgICAgICAgICAgIE5DR3MgPSBtZXRhZGF0YShzcGUpJE5DR3MsIGsgPSAxKQ0KDQpwbG90UGFpclBDQShydXY0LCBhc3NheSA9IDIsIGNvbG9yID0gc2xpZGVfbmFtZSwgc2hhcGUgPSByZWdpb25zLCB0aXRsZSA9ICJSVVY0IHJlbW92ZUJhdGNoIikNCg0KcGxvdFJMRXhwcihydXY0LCBhc3NheSA9IDIsIGNvbG9yID0gYHNsaWRlX25hbWVgKSArIGdndGl0bGUoIlJVVjQgcmVtb3ZlQmF0Y2giKQ0KYGBgDQoNCiMjIExpbW1hDQoNCmBgYHtyIGxpbW1hIGJhdGNoLCAgZmlnLndpZHRoPTEwLGZpZy5oZWlnaHQ9MTAsIGV2YWw9YmF0Y2hfY29ycmVjdGlvbl9uZWVkZWR9DQpsaW1tYSA8LSBnZW9teEJhdGNoQ29ycmVjdGlvbihzcGUsDQogICAgICAgICAgICAgICAgICAgICAgIGJhdGNoID0gY29sRGF0YShzcGUpJGBzbGlkZV9uYW1lYCwgbWV0aG9kID0gIkxpbW1hIiwNCiAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduID0gbW9kZWwubWF0cml4KH4gMCArIEFOTjEgKyByZWdpb25zLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBjb2xEYXRhKHNwZSkpKQ0KDQpwbG90UGFpclBDQShsaW1tYSwgYXNzYXkgPSAyLCBjb2xvciA9IHNsaWRlX25hbWUsIHNoYXBlID0gcmVnaW9ucywgdGl0bGUgPSAiTGltbWEgcmVtb3ZlQmF0Y2giKQ0KDQpwbG90UkxFeHByKGxpbW1hLCBhc3NheSA9IDIsIGNvbG9yID0gc2xpZGVfbmFtZSkgKyBnZ3RpdGxlKCJMaW1tYSByZW1vdmVCYXRjaCIpDQpgYGANCg0KIyMgQ29tYmF0U2VxDQoNCmBgYHtyLCBldmFsPWJhdGNoX2NvcnJlY3Rpb25fbmVlZGVkfQ0KYWRqdXN0ZWQgPC0gQ29tQmF0X3NlcSh0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJleHBycyJdXSwgYmF0Y2g9dGFyZ2V0X0RhdGFAcGhlbm9EYXRhQGRhdGFbWyJzbGlkZV9uYW1lIl1dLCBncm91cD10YXJnZXRfRGF0YUBwaGVub0RhdGFAZGF0YVtbIkFOTjIiXV0pDQphc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAiY29tYmF0IikgPC0gIGFkanVzdGVkDQoNCnVtYXBfb3V0MiA8LQ0KICB1bWFwKHQobG9nMihhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhICwgZWx0ID0gImNvbWJhdCIpKSksDQogICAgICAgY29uZmlnID0gY3VzdG9tX3VtYXApDQpwRGF0YSh0YXJnZXRfRGF0YSlbLCBjKCJVTUFQMSIsICJVTUFQMiIpXSA8LSB1bWFwX291dDIkbGF5b3V0WywgYygxLDIpXQ0KZ2dwbG90KHBEYXRhKHRhcmdldF9EYXRhKSwNCiAgICAgICBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gc2xpZGVfbmFtZSwgc2hhcGUgPSBBTk4xKSkgKw0KDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsNCiAgI2dlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9cm93Lm5hbWVzKHBEYXRhKHRhcmdldF9EYXRhKSkpLCBzaXplPTIsbWF4Lm92ZXJsYXBzID0gMTAwKSsNCiAgdGhlbWVfYncoKSArIGdndGl0bGUoIkNvbWJhdFNlcSByZW1vdmVCYXRjaCIpDQpgYGANCg0KIyMgQ29tcGFyZSBsaW1tYSB2cyBSVVY0DQoNCmBgYHtyIGNvbXBhcmUgYmF0Y2gsIGV2YWw9YmF0Y2hfY29ycmVjdGlvbl9uZWVkZWR9DQpzcGVfbGlzdCA8LSBsaXN0KHNwZSwgcnV2NCwgbGltbWEpDQoNCnBsb3RDbHVzdGVyRXZhbFN0YXRzKHNwZV9saXN0ID0gc3BlX2xpc3QsDQogICAgICAgICAgICAgICAgICAgICBiaW9fZmVhdHVyZV9uYW1lID0gInJlZ2lvbnMiLA0KICAgICAgICAgICAgICAgICAgICAgYmF0Y2hfZmVhdHVyZV9uYW1lID0gInNsaWRlX25hbWUiLA0KICAgICAgICAgICAgICAgICAgICAgZGF0YV9uYW1lcyA9IGMoIlJhdyIsIlJVVjQiLCJMaW1tYSIpKQ0KYGBgDQoNCiMjIEFkZCBiYXRjaCBjb3JyZWN0aW9uIHJlc3VsdCB0byB0YXJnZXRfRGF0YQ0KDQpgYGB7ciBhZGQgYmF0Y2ggY29ycmVjdGlvbiwgZXZhbD1iYXRjaF9jb3JyZWN0aW9uX25lZWRlZH0NCm5lZ19wcm9iZXNfc2F2ZSA8LSB0KGFzLm1hdHJpeCh0YXJnZXRfRGF0YUBhc3NheURhdGEkcV9ub3JtWyJOZWdQcm9iZS1XVFgiLF0pKQ0Kcm93bmFtZXMobmVnX3Byb2Jlc19zYXZlKSA8LSAiTmVnUHJvYmUtV1RYIg0KDQojIERlcGVuZGluZyBvbiBjb3JyZWN0IG1ldGhvZCwgY2hhbmdlIHRoZSB3b3JkICJsaW1tYSIgdG8gInJ1djQiIG9yIHZpY2UgdmVyc2EuDQpsaW1tYSA8LWxpbW1hQGFzc2F5c0BkYXRhQGxpc3REYXRhW1sibG9nY291bnRzIl1dDQpsaW1tYSA8LSByYmluZChsaW1tYSwgbmVnX3Byb2Jlc19zYXZlKQ0KY29sbmFtZXMobGltbWEpIDwtIGNvbG5hbWVzKGFzLmRhdGEuZnJhbWUodGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1sicV9ub3JtIl1dKSkNCmFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJsaW1tYSIpIDwtICBsaW1tYQ0KDQpydXY0IDwtcnV2NEBhc3NheXNAZGF0YUBsaXN0RGF0YVtbImxvZ2NvdW50cyJdXQ0KcnV2NCA8LSByYmluZChydXY0LCBuZWdfcHJvYmVzX3NhdmUpDQpjb2xuYW1lcyhydXY0KSA8LSBjb2xuYW1lcyhhcy5kYXRhLmZyYW1lKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbInFfbm9ybSJdXSkpDQphc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAicnV2NCIpIDwtICBydXY0DQpgYGANCg0KIyMgQ2hvb3NlIG1ldGhvZCB7LmFjdGl2ZX0NCg0KYGBge3J9DQojIGNob29zZSBtZXRob2QgdG8gcmVwbGFjZSBxX25vcm0gb3Igc2V0IGl0IHRvICIiDQptZXRob2QgPC0gIiINCg0KIyByZXBsYWNlIHFfbm9ybSB3aXRoIGNob3NlbiBtZXRob2QNCmlmICghbWV0aG9kID09ICIiKSB7DQogICMgc2F2ZSBvcmdpbmFsIHFfbm9ybQ0KICBhc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAib3JpZ2luYWwiKSA8LSAgYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gInFfbm9ybSIpDQogIA0KICBhc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAicV9ub3JtIikgPC0gIGFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9IG1ldGhvZCkNCiAgcHJpbnQocGFzdGUoIkJhdGNoIGNvcnJlY3Rpb24gbWV0aG9kOiIsIG1ldGhvZCkpDQp9IGVsc2Uge3ByaW50KCJObyBiYXRjaCBjb3JyZWN0aW9uIG5lZWRlZCIpfQ0KYGBgDQoNCiMjIFVtYXAgYWZ0ZXIgYmF0Y2ggY29ycmVjdGlvbg0KDQpgYGB7ciwgZmlnLndpZHRoPTEwLGZpZy5oZWlnaHQ9OCwgZXZhbD1iYXRjaF9jb3JyZWN0aW9uX25lZWRlZH0NCnVtYXBfb3V0IDwtDQogIHVtYXAodChsb2cyKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEgLCBlbHQgPSAicV9ub3JtIikpKSwNCiAgICAgICBjb25maWcgPSBjdXN0b21fdW1hcCkNCnBEYXRhKHRhcmdldF9EYXRhKVssIGMoIlVNQVAxIiwgIlVNQVAyIildIDwtIHVtYXBfb3V0JGxheW91dFssIGMoMSwyKV0NCmdncGxvdChwRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyLCBjb2xvciA9IHNsaWRlX25hbWUsIHNoYXBlID0gQU5OMSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMykgKw0KICAjZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbD1yb3cubmFtZXMocERhdGEodGFyZ2V0X0RhdGEpKSksIHNpemU9MixtYXgub3ZlcmxhcHMgPSAxMDApKw0KICB0aGVtZV9idygpDQpgYGANCg0KIyA2LjMgUnVuIHRTTkUgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KVXNlIHRoZSB0YWItbWVudSB0byBuYXZpZ2F0ZSENCg0KT25lIGNvbW1vbiBhcHByb2FjaCB0byB1bmRlcnN0YW5kaW5nIGhpZ2gtcGxleCBkYXRhIGlzIGRpbWVuc2lvbg0KcmVkdWN0aW9uLiBUd28gY29tbW9uIG1ldGhvZHMgYXJlIFVNQVAgYW5kIHRTTkUsIHdoaWNoIGFyZQ0Kbm9uLW9ydGhvZ29uYWxseSBjb25zdHJhaW5lZCBwcm9qZWN0aW9ucyB0aGF0IGNsdXN0ZXIgc2FtcGxlcyBiYXNlZCBvbg0Kb3ZlcmFsbCBnZW5lIGV4cHJlc3Npb24uIEluIHRoaXMgc3R1ZHksIHdlIHNlZSBieSBlaXRoZXIgVU1BUCAoZnJvbSB0aGUNCnVtYXAgcGFja2FnZSkgb3IgdFNORSAoZnJvbSB0aGUgUnRzbmUgcGFja2FnZSkNCg0KIyMgMQ0KDQpgYGB7ciB0U05FLCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD04fQ0KdHNuZV9vdXQgPC0NCiAgUnRzbmUodChsb2cyKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEgLCBlbHQgPSAicV9ub3JtIikpKSwNCiAgICAgICAgcGVycGxleGl0eSA9IG5jb2wodGFyZ2V0X0RhdGEpKi4xNSkNCnBEYXRhKHRhcmdldF9EYXRhKVssIGMoInRTTkUxIiwgInRTTkUyIildIDwtIHRzbmVfb3V0JFlbLCBjKDEsMildDQpnZ3Bsb3QocERhdGEodGFyZ2V0X0RhdGEpLA0KICAgICAgIGFlcyh4ID0gdFNORTEsIHkgPSB0U05FMiwgc2hhcGUgPSBzbGlkZV9uYW1lLCBjb2xvciA9IEFOTjIpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsNCiAgI2dlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9cm93Lm5hbWVzKHBEYXRhKHRhcmdldF9EYXRhKSkpLCBzaXplPTIsbWF4Lm92ZXJsYXBzID0gMTAwKSsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCiMjIDINCg0KYGBge3IsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTh9DQp0c25lX291dCA8LQ0KICBSdHNuZSh0KGxvZzIoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSAsIGVsdCA9ICJxX25vcm0iKSkpLA0KICAgICAgICBwZXJwbGV4aXR5ID0gbmNvbCh0YXJnZXRfRGF0YSkqLjE1KQ0KcERhdGEodGFyZ2V0X0RhdGEpWywgYygidFNORTEiLCAidFNORTIiKV0gPC0gdHNuZV9vdXQkWVssIGMoMSwyKV0NCmdncGxvdChwRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgYWVzKHggPSB0U05FMSwgeSA9IHRTTkUyLCBjb2xvciA9IEFOTjMsIHNoYXBlID0gQU5OMSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMykgKw0KICAjZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbD1yb3cubmFtZXMocERhdGEodGFyZ2V0X0RhdGEpKSksIHNpemU9MixtYXgub3ZlcmxhcHMgPSAxMDApKw0KICB0aGVtZV9idygpDQpgYGANCg0KIyA2LjQgQ2x1c3RlcmluZyBoaWdoIENWIEdlbmVzIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNCkFub3RoZXIgYXBwcm9hY2ggdG8gZXhwbG9yZSB0aGUgZGF0YSBpcyB0byBjYWxjdWxhdGUgdGhlIGNvZWZmaWNpZW50IG9mDQp2YXJpYXRpb24gKCRDViQpIGZvciBlYWNoIGdlbmUgKCRnJCkgdXNpbmcgdGhlIGZvcm11bGENCiRDVl9nPVNEX2cvbWVhbl9nJC4gV2UgdGhlbiBpZGVudGlmeSBnZW5lcyB3aXRoIGhpZ2ggQ1ZzIHRoYXQgc2hvdWxkDQpoYXZlIGxhcmdlIGRpZmZlcmVuY2VzIGFjcm9zcyB0aGUgdmFyaW91cyBwcm9maWxlZCBzZWdtZW50cy4gVGhpcw0KdW5iaWFzZWQgYXBwcm9hY2ggY2FuIHJldmVhbCBoaWdobHkgdmFyaWFibGUgZ2VuZXMgYWNyb3NzIHRoZSBzdHVkeS4NCg0KV2UgcGxvdCB0aGUgcmVzdWx0cyB1c2luZyB1bnN1cGVydmlzZWQgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcsDQpkaXNwbGF5ZWQgYXMgYSBoZWF0bWFwLg0KDQpgYGB7ciBjbHVzdGVyaW5nMX0NCiMgY3JlYXRlIGEgbG9nMiB0cmFuc2Zvcm0gb2YgdGhlIGRhdGEgZm9yIGFuYWx5c2lzDQphc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAibG9nX3EiKSA8LQ0KICBhc3NheURhdGFBcHBseSh0YXJnZXRfRGF0YSwgMiwgRlVOID0gbG9nLCBiYXNlID0gMiwgZWx0ID0gInFfbm9ybSIpDQoNCiMgY3JlYXRlIENWIGZ1bmN0aW9uDQpjYWxjX0NWIDwtIGZ1bmN0aW9uKHgpIHtzZCh4KSAvIG1lYW4oeCl9DQpDVl9kYXQgPC0gYXNzYXlEYXRhQXBwbHkodGFyZ2V0X0RhdGEsDQogICAgICAgICAgICAgICAgICAgICAgICAgZWx0ID0gImxvZ19xIiwgTUFSR0lOID0gMSwgY2FsY19DVikNCiMgc2hvdyB0aGUgaGlnaGVzdCBDRCBnZW5lcyBhbmQgdGhlaXIgQ1YgdmFsdWVzDQpzb3J0KENWX2RhdCwgZGVjcmVhc2luZyA9IFRSVUUpWzE6NV0NCmBgYA0KDQojIyBUYWJsZSBvZiBDViB2YWx1ZXMNCg0KYGBge3J9DQojIHNob3cgdGhlIGhpZ2hlc3QgQ0QgZ2VuZXMgYW5kIHRoZWlyIENWIHZhbHVlcw0KZGF0YXRhYmxlKGFzLmRhdGEuZnJhbWUoQ1ZfZGF0KSwNCiAgICAgICAgICBleHRlbnNpb25zID0gJ0J1dHRvbnMnLCBvcHRpb25zID0gbGlzdCAoDQogICAgICAgICAgICBvcmRlciA9IGxpc3QoMSwgJ2Rlc2MnKSwNCiAgICAgICAgICAgIGRvbSA9ICdCZnRyaXAnLA0KICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpDQogICAgICAgICAgKSwgY2FwdGlvbiA9ICJDViB2YWx1ZXMgb2YgZ2VuZXMiIA0KKSAlPiUgZm9ybWF0Um91bmQoY29sdW1ucz1jKCJDVl9kYXQiKSwgZGlnaXRzPTMpDQoNCmBgYA0KDQojIyBIZWF0bWFwIGdlbmVzIGluIHRoZSB0b3AgM3JkIG9mIHRoZSBDViB2YWx1ZXMgey5hY3RpdmV9DQoNCmBgYHtyIENWaGVhdG1hcCwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTV9DQpHT0kgPC0gbmFtZXMoQ1ZfZGF0KVtDVl9kYXQgPiBxdWFudGlsZShDVl9kYXQsIDAuNzUpXQ0KDQpwaGVhdG1hcChhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhW0dPSSwgXSwgZWx0ID0gImxvZ19xIiksDQogICAgICAgICBzY2FsZSA9ICJyb3ciLA0KICAgICAgICAgY3V0cmVlX2NvbHMgPSAzLA0KICAgICAgICAgY3V0cmVlX3Jvd3MgPSAyLA0KICAgICAgICAgc2hvd19yb3duYW1lcyA9IEZBTFNFLCBzaG93X2NvbG5hbWVzID0gVFJVRSwNCiAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLA0KICAgICAgICAgZHJvcF9sZXZlbHMgPSBUUlVFLA0KICAgICAgICAgY2x1c3RlcmluZ19tZXRob2QgPSAiYXZlcmFnZSIsDQogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgIGJyZWFrcyA9IHNlcSgtMywgMywgMC4wNSksDQogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygicHVycGxlMyIsICJibGFjayIsICJ5ZWxsb3cyIikpKDEyMCksDQogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGNvbG9yX2xpc3QsDQogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IHBEYXRhKHRhcmdldF9EYXRhKVssIGFubl9uYW1lc10pDQoNCmBgYA0KDQpgYGB7ciBsb2dfdHJhbnNmb3JtfQ0KYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gImxvZ19xIikgPC0gIGFzc2F5RGF0YUFwcGx5KHRhcmdldF9EYXRhLCAyLCBGVU4gPSBsb2csIGJhc2UgPSAyLCBlbHQgPSAicV9ub3JtIikNCmxvZ19xIDwtYXMuZGF0YS5mcmFtZShhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhLCBlbHQ9ICJsb2dfcSIpKQ0KI2JhdGNoIDwtYXMuZGF0YS5mcmFtZShhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhLCBlbHQ9ICJiYXRjaCIpKQ0KYGBgDQoNCiMgNi41LjAgQ3JlYXRlIHN1YnNldCBvZiBkYXRhDQoNCmBgYHtyIGRlZmluZV9hY3RpdmVfYW9pc30NCiMgZGV0ZXJtaW5lIEFPSXMgdG8gdXNlDQojYWN0aXZlX2FvaXM8LXJvd25hbWVzKGFubilbYW5uJHBhdGllbnQ9PSJwNCJdDQphY3RpdmVfYW9pczwtIG5hbWVzKGFzLmRhdGEuZnJhbWUoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSwgZWx0PSAiZXhwcnMiKSkpDQpgYGANCg0KIyA2LjUuMSBDbHVzdGVyaW5nIGhpZ2ggQ1YgZ2VuZXMgZm9yIHN1YnNldCB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpDYWxjdWxhdGluZyBDViB2YWx1ZXMNCg0KYGBge3IgY2x1c3RlcmluZ19zdWJzZXQsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTE1fQ0KIyBjcmVhdGUgYSBsb2cyIHRyYW5zZm9ybSBvZiB0aGUgZGF0YSBmb3IgYW5hbHlzaXMNCmFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJsb2dfcSIpIDwtDQogIGFzc2F5RGF0YUFwcGx5KHRhcmdldF9EYXRhLCAyLCBGVU4gPSBsb2csIGJhc2UgPSAyLCBlbHQgPSAicV9ub3JtIikNCg0KIyBjcmVhdGUgQ1YgZnVuY3Rpb24NCmNhbGNfQ1YgPC0gZnVuY3Rpb24oeCkge3NkKHgpIC8gbWVhbih4KX0NCkNWX2RhdCA8LSBhc3NheURhdGFBcHBseSh0YXJnZXRfRGF0YVssYWN0aXZlX2FvaXNdLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGVsdCA9ICJsb2dfcSIsIE1BUkdJTiA9IDEsIGNhbGNfQ1YpDQoNCmBgYA0KDQojIyBUYWJsZSBvZiBDViB2YWx1ZXMNCg0KYGBge3J9DQojIHNob3cgdGhlIGhpZ2hlc3QgQ0QgZ2VuZXMgYW5kIHRoZWlyIENWIHZhbHVlcw0KZGF0YXRhYmxlKGFzLmRhdGEuZnJhbWUoQ1ZfZGF0KSwNCiAgICAgICAgICBleHRlbnNpb25zID0gJ0J1dHRvbnMnLCBvcHRpb25zID0gbGlzdCAoDQogICAgICAgICAgICBvcmRlciA9IGxpc3QoMSwgJ2Rlc2MnKSwNCiAgICAgICAgICAgIGRvbSA9ICdCZnRyaXAnLA0KICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpDQogICAgICAgICAgKSwgY2FwdGlvbiA9ICJDViB2YWx1ZXMgb2YgZ2VuZXMiIA0KKSAlPiUgZm9ybWF0Um91bmQoY29sdW1ucz1jKCJDVl9kYXQiKSwgZGlnaXRzPTMpDQoNCmBgYA0KDQojIyBIZWF0bWFwIG9uIG9mIHN1YnNldCwgZ2VuZXMgaW4gdGhlIHRvcCAzcmQgb2YgdGhlIENWIHZhbHVlcyB7LmFjdGl2ZX0NCg0KYGBge3IsIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTE1fQ0KIyBJZGVudGlmeSBnZW5lcyBpbiB0aGUgdG9wIDNyZCBvZiB0aGUgQ1YgdmFsdWVzDQpHT0kgPC0gbmFtZXMoQ1ZfZGF0KVtDVl9kYXQgPiBxdWFudGlsZShDVl9kYXQsIDAuNzUpXQ0KcGhlYXRtYXAoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVtHT0ksYWN0aXZlX2FvaXMgXSwgZWx0ID0gImxvZ19xIiksDQogICAgICAgIHNjYWxlID0gInJvdyIsDQogICAgICAgIGZvbnRzaXplX3JvdyA9IDUsDQogICAgICAgIGN1dHJlZV9jb2xzID0gMywNCiAgICAgICAgY3V0cmVlX3Jvd3MgPSAyLA0KICAgICAgICBzaG93X3Jvd25hbWVzID0gRkFMU0UsIHNob3dfY29sbmFtZXMgPSBUUlVFLA0KICAgICAgICBib3JkZXJfY29sb3IgPSBOQSwNCiAgICAgICAgY2x1c3RlcmluZ19tZXRob2QgPSAiYXZlcmFnZSIsDQogICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgIGJyZWFrcyA9IHNlcSgtMywgMywgMC4wNSksDQogICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJwdXJwbGUzIiwgImJsYWNrIiwgInllbGxvdzIiKSkoMTIwKSwNCiAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGNvbG9yX2xpc3QsDQogICAgICAgIGFubm90YXRpb25fY29sID0NCiAgICAgICAgICBwRGF0YSh0YXJnZXRfRGF0YSlbLCBhbm5fbmFtZXNdKQ0KYGBgDQoNCiMgNy4xIERpZmZlcmVudGlhbCBFeHByZXNzaW9uDQoNCiFbXShMOi9wa2xvb3N0ZXJtYW4vS2lkbmV5X29yZ2FuX2F0bGFzL2tpZG5leV9zdHJ1Y3R1cmUucG5nKQ0KDQojIyB0LXRlc3QNCg0KYGBge3IgdHRlc3R9DQojZ2MoKQ0KcGxvdHM8LWxpc3QoKQ0KdGFibGVzPC1saXN0KCkNCmxhYmVsczwtbGlzdCgpDQp0ZXN0PC0idHRlc3QiDQptdGM8LSJCWSINCiNvcHRpb25zOiAiaG9sbSIgICAgICAgImhvY2hiZXJnIiAgICJob21tZWwiICAgICAiYm9uZmVycm9uaSIgIkJIIiAgICAgICAgICJCWSIgICAgICAgICAiZmRyIiANCmNvdW50ZXI9MQ0KDQpjb21wc19kZjwtZGF0YS5mcmFtZShjb21wPScnLHZhbD0nJykNCg0KZm9yKHJlZ2lvbiBpbiB1bmlxdWUocERhdGEodGFyZ2V0X0RhdGEpJEFOTjMpKSB7DQogIGZvciAoYWN0aXZlX2dyb3VwMSBpbiB1bmlxdWUocERhdGEodGFyZ2V0X0RhdGEpJEFOTjEpKSB7DQogICAgZm9yIChhY3RpdmVfZ3JvdXAyIGluIHVuaXF1ZShwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMSkpIHsNCiAgICAgIA0KICAgICAgI3N1cHJlc3MgcmVkdW5jYW50IGNvbXBhcmVzDQogICAgICBpZihhY3RpdmVfZ3JvdXAxPT1hY3RpdmVfZ3JvdXAyKSB7bmV4dH0NCiAgICAgIGNvbXA8LXBhc3RlKHNvcnQoYyhyZWdpb24sIGFjdGl2ZV9ncm91cDEsYWN0aXZlX2dyb3VwMikpLGNvbGxhcHNlID0gIl8iKQ0KICAgICAgaWYoY29tcCAlaW4lIGNvbXBzX2RmJGNvbXApIHtuZXh0fQ0KICAgICAgdGVtcF9kZjwtZGF0YS5mcmFtZShjb21wPWNvbXAgLHZhbD0xKQ0KICAgICAgY29tcHNfZGY8LXJiaW5kKGNvbXBzX2RmLHRlbXBfZGYpDQogICAgICANCiAgICAgIGxhYmVsc1tbY291bnRlcl1dPC1wYXN0ZShhY3RpdmVfZ3JvdXAxLCIgdnMgIiwgYWN0aXZlX2dyb3VwMikNCiAgICAgIGdyb3VwMTwtbG9nX3FbLG5hbWVzKGFzLmRhdGEuZnJhbWUoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSwgZWx0PSAiZXhwcnMiKSkpW3BEYXRhKHRhcmdldF9EYXRhKSRBTk4xPT1hY3RpdmVfZ3JvdXAxICYgcERhdGEodGFyZ2V0X0RhdGEpJEFOTjM9PXJlZ2lvbl1dDQogICAgICBncm91cDI8LWxvZ19xWyxuYW1lcyhhcy5kYXRhLmZyYW1lKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEsIGVsdD0gImV4cHJzIikpKVtwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMT09YWN0aXZlX2dyb3VwMiAmIHBEYXRhKHRhcmdldF9EYXRhKSRBTk4zPT1yZWdpb25dXQ0KICAgICAgDQogICAgICAjcnVuIHRfdGVzdHMgIA0KICAgICAgcmVzdWx0czwtYXMuZGF0YS5mcmFtZSAoIGFwcGx5KGxvZ19xLCAxLCBmdW5jdGlvbih4KSB0LnRlc3QoeFtjb2xuYW1lcyhncm91cDEpXSx4W2NvbG5hbWVzKGdyb3VwMildKSRwLnZhbHVlKSApDQogICAgICBjb2xuYW1lcyhyZXN1bHRzKTwtInJhd19wX3ZhbHVlIg0KICAgICAgDQogICAgICAjbXVsdGlwbGVfdGVzdGluZ19jb3JyZWN0aW9uDQogICAgICBhZGpfcF92YWx1ZTwtIHAuYWRqdXN0KHJlc3VsdHMkcmF3X3BfdmFsdWUsbWV0aG9kPW10YykNCiAgICAgIHJlc3VsdHM8LWNiaW5kKHJlc3VsdHMsYWRqX3BfdmFsdWUpDQogICAgICANCiAgICAgICNjYWxjX2Zkcg0KICAgICAgRkRSPC0gcC5hZGp1c3QocmVzdWx0cyRyYXdfcF92YWx1ZSxtZXRob2Q9ImZkciIpDQogICAgICByZXN1bHRzPC1jYmluZChyZXN1bHRzLEZEUikNCiAgICAgIA0KICAgICAgI2ZvbGRfY2hhbmdlcw0KICAgICAgI2FzIGJhc2UgZGF0YSBpcyBhbHJlYWR5IGxvZyB0cmFuc2Zvcm1lZCwgbWVhbnMgbmVlZCB0byBiZSBzdWJ0cmFjdGVkIHRvIGdldCBGQyBpbiBsb2cgc3BhY2UNCiAgICAgIGZjaGFuZ2VzPC1hcy5kYXRhLmZyYW1lKGFwcGx5KGxvZ19xLCAxLCBmdW5jdGlvbih4KSAobWVhbih4W2NvbG5hbWVzKGdyb3VwMSldKSAtIG1lYW4oeFtjb2xuYW1lcyhncm91cDIpXSkpKSkNCiAgICAgIGNvbG5hbWVzKGZjaGFuZ2VzKTwtIkZDIg0KICAgICAgI3Bhc3RlKCJGQyIsYWN0aXZlX2dyb3VwMSwiIC8gIixhY3RpdmVfZ3JvdXAyKQ0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxmY2hhbmdlcykNCiAgICAgIA0KICAgICAgI2FkZCBnZW5lbmFtZXMNCiAgICAgIHJlc3VsdHMkR2VuZTwtcm93bmFtZXMocmVzdWx0cykNCiAgICAgIA0KICAgICAgI3NldCBjYXRlZ29yaWVzIGJhc2VkIG9uIFAtdmFsdWUgJiBGRFIgZm9yIHBsb3R0aW5nDQogICAgICByZXN1bHRzJENvbG9yIDwtICJOUyBvciBGQyA8IDEiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkcmF3X3BfdmFsdWUgPCAwLjA1XSA8LSAiUCA8IDAuMDUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkRkRSIDwgMC4wNV0gPC0gIkZEUiA8IDAuMDUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkRkRSIDwgMC4wMDFdIDwtICJGRFIgPCAwLjAwMSINCiAgICAgIHJlc3VsdHMkQ29sb3JbYWJzKHJlc3VsdHMkRkMpIDwxXSA8LSAiTlMgb3IgRkMgPCAxIg0KICAgICAgcmVzdWx0cyRDb2xvciA8LSBmYWN0b3IocmVzdWx0cyRDb2xvciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk5TIG9yIEZDIDwgMSIsICJQIDwgMC4wNSIsICJGRFIgPCAwLjA1IiwgIkZEUiA8IDAuMDAxIikpDQogICAgICANCiAgICAgICN2dWxjYW5vcGxvdA0KICAgICAgDQogICAgICAjIHBpY2sgdG9wIGdlbmVzIGZvciBlaXRoZXIgc2lkZSBvZiB2b2xjYW5vIHRvIGxhYmVsDQogICAgICAjIG9yZGVyIGdlbmVzIGZvciBjb252ZW5pZW5jZToNCiAgICAgIA0KICAgICAgcmVzdWx0cyRpbnZlcnRfUCA8LSAoLWxvZzEwKHJlc3VsdHMkYWRqX3BfdmFsdWUpKSAqIHNpZ24ocmVzdWx0cyRGQykNCiAgICAgIHRvcF9nIDwtIGMoKQ0KICAgICAgdG9wX2cgPC0gYyh0b3BfZywNCiAgICAgICAgICAgICAgICAgcmVzdWx0c1ssICdHZW5lJ11bDQogICAgICAgICAgICAgICAgICAgb3JkZXIocmVzdWx0c1ssICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gVFJVRSlbMToyMF1dLA0KICAgICAgICAgICAgICAgICByZXN1bHRzWywgJ0dlbmUnXVtvcmRlcihyZXN1bHRzWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToyMF1dKQ0KICAgICAgdG9wX2c8LSB1bmlxdWUodG9wX2cpDQogICAgICByZXN1bHRzIDwtIHJlc3VsdHNbLCAtMSpuY29sKHJlc3VsdHMpXSAjIHJlbW92ZSBpbnZlcnRfUCBmcm9tIG1hdHJpeA0KICAgICAgDQogICAgICAjIEdyYXBoIHJlc3VsdHMNCiAgICAgIA0KICAgICAgcGxvdHNbW2NvdW50ZXJdXTwtIGdncGxvdChyZXN1bHRzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IEZDLCB5ID0gLWxvZzEwKHJhd19wX3ZhbHVlKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gQ29sb3IsIGxhYmVsID0gR2VuZSkpICsNCiAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygxLCAtMSksIGx0eSA9ICJkYXNoZWQiKSArDQogICAgICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgICAgZ2VvbV9wb2ludCgpICsNCiAgICAgICAgbGFicyh4ID0gcGFzdGUoIkVucmljaGVkIGluIiwgYWN0aXZlX2dyb3VwMiwiIDwtIGxvZzIoRkMpIC0+IEVucmljaGVkIGluIiwgYWN0aXZlX2dyb3VwMSksDQogICAgICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKw0KICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhgRkRSIDwgMC4wMDFgID0gImRvZGdlcmJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFAgPCAwLjA1YCA9ICJvcmFuZ2UyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYE5TIG9yIEZDIDwgMC41YCA9ICJncmF5IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgICAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHN1YnNldChyZXN1bHRzLCAoLTE+RkN8IEZDPjEpICYgRkRSIDwgMC4wNSAmIEdlbmUgJWluJSB0b3BfZyksDQogICAgICAgICAgICAgICAgICAgICAgICBwb2ludC5wYWRkaW5nID0gMC4xNSwgY29sb3IgPSAiYmxhY2siLCBzaXplPTMuNSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5zZWdtZW50Lmxlbmd0aCA9IC4xLCBib3gucGFkZGluZyA9IC4yLCBsd2QgPSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgbWF4Lm92ZXJsYXBzID0gNTApICsNCiAgICAgICAgdGhlbWVfYncoYmFzZV9zaXplID0gMTApICsNCiAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgICAgICAgZ2d0aXRsZShwYXN0ZSgiY2xhc3M6ICIsIHJlZ2lvbiwiIC0gIiwgdGVzdCwgbXRjLCJtdWx0aXRlc3QgY29yciIpKQ0KICAgICAgDQogICAgICAjc3RvcmUgdGFibGVzIGZvciBkaXNwbGF5IGxhdGVyDQogICAgICB0YWJsZXNbW2NvdW50ZXJdXTwtcmVzdWx0cw0KICAgICAgDQogICAgICBjb3VudGVyID0gY291bnRlcisxDQogICAgICAjZGF0YXRhYmxlKHN1YnNldChyZXN1bHRzLCBHZW5lICVpbiUgR09JKSwgcm93bmFtZXM9RkFMU0UsY2FwdGlvbiA9IHBhc3RlKCJERSByZXN1bHRzICIsIGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKSkNCiAgICB9DQogIH0NCn0NCmdyaWQuYXJyYW5nZShncm9icz1wbG90cyxuY29sPTIpDQpgYGANCg0KYGBge3IgZHVtcF90YWJsZXMscmVzdWx0cz0nYXNpcyd9DQojc3RyYW5nbHkgZG9lcyBub3QgYXBwZWFyIGluIGh0bWwgb3V0cHV0IC0+IGNodWNrcyBhcmUgbGltaXRlZCB0byBvbmUgZGF0YXRhYmxlIHBlciBjaHVjay4gVGhlIGh0bWx0b29scyBhcmUgYSB3YXkgYXJvdW5kIHRoaXMgYnV0IGRvZXMgc2hvdyB0aGVtIHN0YWNrZWQgaW4gYSBib3guDQpyZXN1bHRzX3RhYmxlcyA8LSBodG1sdG9vbHM6OnRhZ0xpc3QoKQ0KZm9yIChjIGluICgyOmNvdW50ZXItMSkpIHsNCiAgI0dlbmUgJWluJSBHT0kNCiAgcmVzdWx0c190YWJsZXNbW2NdXSA8LSBkYXRhdGFibGUoc3Vic2V0KHRhYmxlc1tbY11dLCBDb2xvciA9PSBjKCJGRFIgPCAwLjAwMSIsICAiUCA8IDAuMDUiKSksIA0KICAgICAgICAgICByb3duYW1lcz1GQUxTRSwNCiAgICAgICAgICAgZXh0ZW5zaW9ucyA9IGMoJ0J1dHRvbnMnKSwgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgICBkb20gPSAnQmZ0cmlwJywNCiAgICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpDQogICAgICAgICAgICApLCBoZWlnaHQgPSA1NTAsDQogICAgICAgICAgIGNhcHRpb24gPSBwYXN0ZSgiREUgcmVzdWx0cyAiLCBsYWJlbHNbW2NdXSksZmlsdGVyPSd0b3AnKSAlPiUgZm9ybWF0Um91bmQoY29sdW1ucz1jKCJyYXdfcF92YWx1ZSIsImFkal9wX3ZhbHVlIiwiRkRSIiwiRkMiKSwgZGlnaXRzPTMpDQogICNjYXQoJ1xuXG48IS0tIC0tPlxuXG4nKQ0KfSAgICAgICAgICAgIA0KcmVzdWx0c190YWJsZXMNCmBgYA0KDQpgYGB7cn0NCnByaW50KCIiKSAjIGZvciBsYXlvdXQgb2YgbmV4dCBzZWN0aW9uDQpgYGANCg0KIyA3LjIgREUgYW5hbHlzaXMgd2l0aCBMTU0NCg0KQSBjb21tb24gc3RhdGlzdGljYWwgYXBwcm9hY2ggaXMgdG8gdXNlIGEgbGluZWFyIG1peGVkLWVmZmVjdCBtb2RlbA0KKExNTSkuIFRoZSBMTU0gYWxsb3dzIHRoZSB1c2VyIHRvIGFjY291bnQgZm9yIHRoZSBzdWJzYW1wbGluZyBwZXINCnRpc3N1ZTsgaW4gb3RoZXIgd29yZHMsIHdlIGFkanVzdCBmb3IgdGhlIGZhY3QgdGhhdCB0aGUgbXVsdGlwbGUgcmVnaW9ucw0Kb2YgaW50ZXJlc3QgcGxhY2VkIHBlciB0aXNzdWUgc2VjdGlvbiBhcmUgbm90IGluZGVwZW5kZW50IG9ic2VydmF0aW9ucywNCmFzIGlzIHRoZSBhc3N1bXB0aW9uIHdpdGggb3RoZXIgdHJhZGl0aW9uYWwgc3RhdGlzdGljYWwgdGVzdHMuIFRoZQ0KZm9ybXVsYXRpb24gb2YgdGhlIExNTSBtb2RlbCBkZXBlbmRzIG9uIHRoZSBzY2llbnRpZmljIHF1ZXN0aW9uIGJlaW5nDQphc2tlZC4NCg0KT3ZlcmFsbCwgdGhlcmUgYXJlIHR3byBmbGF2b3JzIG9mIHRoZSBMTU0gbW9kZWwgd2hlbiB1c2VkIHdpdGggR2VvTXgNCmRhdGE6IGkpIHdpdGggYW5kIGlpKSB3aXRob3V0IHJhbmRvbSBzbG9wZS4NCg0KV2hlbiBjb21wYXJpbmcgZmVhdHVyZXMgdGhhdCBjby1leGlzdCBpbiBhIGdpdmVuIHRpc3N1ZSBzZWN0aW9uLCBhDQpyYW5kb20gc2xvcGUgaXMgaW5jbHVkZWQgaW4gdGhlIExNTSBtb2RlbC4gV2hlbiBjb21wYXJpbmcgZmVhdHVyZXMgdGhhdA0KYXJlIG11dHVhbGx5IGV4Y2x1c2l2ZSBpbiBhIGdpdmVuIHRpc3N1ZSBzZWN0aW9uIHRoZSBMTU0gbW9kZWwgZG9lcyBub3QNCnJlcXVpcmUgYSByYW5kb20gc2xvcGUuDQoNCk1vc3RseSwgd2UgdXNlIHBhdGllbnQvc2FtcGxlIGFzIFJhbmRvbSBJbnRlcmNlcHQgd2hlbiB0aGV5IGFyZSBjb21iaW5lZA0Kb24gc2xpZGVzLg0KDQohW10oaHR0cDovL3VzZXEubmwvd3AtY29udGVudC91cGxvYWRzLzIwMjIvMTIvTE1NX3NldHVwLnBuZykNCg0KYGBge3IgTE1NIHJlZ2lvbn0NCiMgY29udmVydCB0ZXN0IHZhcmlhYmxlcyB0byBmYWN0b3JzDQpwRGF0YSh0YXJnZXRfRGF0YSkkdGVzdENsYXNzIDwtIGZhY3RvcihwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMSwgdW5pcXVlKHRhcmdldF9EYXRhJEFOTjEpKQ0KcERhdGEodGFyZ2V0X0RhdGEpW1sic2xpZGUiXV0gPC0gZmFjdG9yKHBEYXRhKHRhcmdldF9EYXRhKVtbInNsaWRlX25hbWUiXV0pDQojYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gImxvZ19xIikgPC0gYXNzYXlEYXRhQXBwbHkodGFyZ2V0X0RhdGEsIDIsIEZVTiA9IGxvZywgYmFzZSA9IDIsIGVsdCA9ICJxX25vcm0iKQ0KDQojIHJ1biBMTU06DQojIGZvcm11bGEgZm9sbG93cyBjb252ZW50aW9ucyBkZWZpbmVkIGJ5IHRoZSBsbWU0IHBhY2thZ2UNCmxtbV9yZXN1bHRzIDwtIGMoKQ0KZm9yIChyZWdpb24gaW4gdW5pcXVlKHBEYXRhKHRhcmdldF9EYXRhKSRBTk4zKSkgew0KICAgIGluZCA8LSBwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMyA9PSByZWdpb24NCg0KICAgIG1peGVkT3V0bWMgPC0NCiAgICAgICAgbWl4ZWRNb2RlbERFKHRhcmdldF9EYXRhWyxpbmRdLA0KICAgICAgICAgICAgICAgICAgICAgZWx0ID0gImxvZ19xIiwNCiAgICAgICAgICAgICAgICAgICAgIG1vZGVsRm9ybXVsYSA9IH4gdGVzdENsYXNzICsgKDEgfCBzbGlkZSksDQogICAgICAgICAgICAgICAgICAgICBncm91cFZhciA9ICJ0ZXN0Q2xhc3MiLA0KICAgICAgICAgICAgICAgICAgICAgbkNvcmVzID0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCksDQogICAgICAgICAgICAgICAgICAgICBtdWx0aUNvcmUgPSBUUlVFKQ0KDQogICAgIyBmb3JtYXQgcmVzdWx0cyBhcyBkYXRhLmZyYW1lDQogICAgcl90ZXN0IDwtIGRvLmNhbGwocmJpbmQsIG1peGVkT3V0bWNbImxzbWVhbnMiLCBdKQ0KICAgIHRlc3RzIDwtIHJvd25hbWVzKHJfdGVzdCkNCiAgICByX3Rlc3QgPC0gYXMuZGF0YS5mcmFtZShyX3Rlc3QpDQogICAgcl90ZXN0JENvbnRyYXN0IDwtIHRlc3RzDQoNCiAgICAjIHVzZSBsYXBwbHkgaW4gY2FzZSB5b3UgaGF2ZSBtdWx0aXBsZSBsZXZlbHMgb2YgeW91ciB0ZXN0IGZhY3RvciB0bw0KICAgICMgY29ycmVjdGx5IGFzc29jaWF0ZSBnZW5lIG5hbWUgd2l0aCBpdCdzIHJvdyBpbiB0aGUgcmVzdWx0cyB0YWJsZQ0KICAgIHJfdGVzdCRHZW5lIDwtDQogICAgICAgIHVubGlzdChsYXBwbHkoY29sbmFtZXMobWl4ZWRPdXRtYyksDQogICAgICAgICAgICAgICAgICAgICAgcmVwLCBucm93KG1peGVkT3V0bWNbImxzbWVhbnMiLCBdW1sxXV0pKSkNCiAgICByX3Rlc3QkU3Vic2V0IDwtIHJlZ2lvbg0KICAgIHJfdGVzdCRGRFIgPC0gcC5hZGp1c3Qocl90ZXN0JGBQcig+fHR8KWAsIG1ldGhvZCA9ICJmZHIiKQ0KICAgIHJfdGVzdCA8LSByX3Rlc3RbLCBjKCJHZW5lIiwgIlN1YnNldCIsICJDb250cmFzdCIsICJFc3RpbWF0ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIlByKD58dHwpIiwgIkZEUiIpXQ0KICAgIA0KICAgIGxtbV9yZXN1bHRzIDwtIHJiaW5kKGxtbV9yZXN1bHRzLCByX3Rlc3QpDQp9DQpgYGANCg0KIyA3LjMgVnVsY2Fub3Bsb3QgKyB0YWJsZSBvZiBMTU0NCg0KYGBge3IgbG1tX3Z1bGNhbm8sIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTEwfQ0KIyBDYXRlZ29yaXplIFJlc3VsdHMgYmFzZWQgb24gUC12YWx1ZSAmIEZEUiBmb3IgcGxvdHRpbmcNCmZjX3RocmVzaG9sZCA9IDENCg0KbG1tX3Jlc3VsdHMkQ29sb3IgPC0gcGFzdGUoIk5TIG9yIEZDIDwgIixmY190aHJlc2hvbGQgPSAxKQ0KbG1tX3Jlc3VsdHMkQ29sb3JbbG1tX3Jlc3VsdHMkYFByKD58dHwpYCA8IDAuMDVdIDwtICJQIDwgMC4wNSINCmxtbV9yZXN1bHRzJENvbG9yW2xtbV9yZXN1bHRzJEZEUiA8IDAuMDVdIDwtICJGRFIgPCAwLjA1Ig0KbG1tX3Jlc3VsdHMkQ29sb3JbbG1tX3Jlc3VsdHMkRkRSIDwgMC4wMDFdIDwtICJGRFIgPCAwLjAwMSINCmxtbV9yZXN1bHRzJENvbG9yW2FicyhsbW1fcmVzdWx0cyRFc3RpbWF0ZSkgPCBmY190aHJlc2hvbGRdIDwtIHBhc3RlKCJOUyBvciBGQyA8ICIsZmNfdGhyZXNob2xkID0gMSkNCmxtbV9yZXN1bHRzJENvbG9yIDwtIGZhY3RvcihsbW1fcmVzdWx0cyRDb2xvciwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk5TIG9yIEZDIDwgMSIsICJQIDwgMC4wNSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGRFIgPCAwLjA1IiwgIkZEUiA8IDAuMDAxIikpDQpjb3VudGVyID0gMQ0KcGxvdHNfbG1tIDwtIGxpc3QoKQ0KZm9yKGMgaW4gdW5pcXVlKGxtbV9yZXN1bHRzJFN1YnNldCkgKSB7DQogIGxtbV9yZXN1bHRzX3NsaWNlID0gbG1tX3Jlc3VsdHNbbG1tX3Jlc3VsdHMkU3Vic2V0ID09IGMsXQ0KDQogICMgcGljayB0b3AgZ2VuZXMgZm9yIGVpdGhlciBzaWRlIG9mIHZvbGNhbm8gdG8gbGFiZWwNCiAgIyBvcmRlciBnZW5lcyBmb3IgY29udmVuaWVuY2U6DQogIGxtbV9yZXN1bHRzX3NsaWNlJGludmVydF9QIDwtICgtbG9nMTAobG1tX3Jlc3VsdHNfc2xpY2UkYFByKD58dHwpYCkpICogc2lnbihsbW1fcmVzdWx0c19zbGljZSRFc3RpbWF0ZSkNCiAgDQogICNsb29wIGhlcmUgb3ZlciB0ZXN0ZWQgY29uZGl0aW9ucyBpZiBhcHBsaWNhYmxlDQogIHRvcF9nIDwtIGMoKQ0KICB0b3BfZyA8LSBjKHRvcF9nLA0KICAgIGxtbV9yZXN1bHRzX3NsaWNlWywgJ0dlbmUnXVsNCiAgICAgIG9yZGVyKGxtbV9yZXN1bHRzX3NsaWNlWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjE1XV0sDQogICAgbG1tX3Jlc3VsdHNfc2xpY2VbLCAnR2VuZSddWw0KICAgICAgb3JkZXIobG1tX3Jlc3VsdHNfc2xpY2VbLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IEZBTFNFKVsxOjE1XV0pDQoNCiAgdG9wX2cgPC0gdW5pcXVlKHRvcF9nKQ0KICANCiAgbG1tX3Jlc3VsdHNfc2xpY2UgPC0gbG1tX3Jlc3VsdHNfc2xpY2VbLCAtMSpuY29sKGxtbV9yZXN1bHRzX3NsaWNlKV0gIyByZW1vdmUgaW52ZXJ0X1AgZnJvbSBtYXRyaXgNCiAgDQogICMgRmxpcCBDb250cmFzdA0KICBjb250cmFzdF9sYWIgPC0gYXMuY2hhcmFjdGVyKGxtbV9yZXN1bHRzX3NsaWNlJENvbnRyYXN0KQ0KICBjb250cmFzdF9sYWIgPC0gc3Ryc3BsaXQoY29udHJhc3RfbGFiLCAiLSIpW1sxXV0NCiAgY29udHJhc3RfbGFiIDwtIHBhc3RlKGNvbnRyYXN0X2xhYlsyXSwgIi0iLCBjb250cmFzdF9sYWJbMV0pDQogIA0KICAjIEdyYXBoIHJlc3VsdHMNCiAgcGxvdHNfbG1tW1tjb3VudGVyXV0gPC0gZ2dwbG90KGxtbV9yZXN1bHRzX3NsaWNlLA0KICAgICAgICAgICAgICAgYWVzKHggPSBFc3RpbWF0ZSwgeSA9IC1sb2cxMChgUHIoPnx0fClgKSwNCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IENvbG9yLCBsYWJlbCA9IEdlbmUpKSArDQogICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYyhmY190aHJlc2hvbGQsIC1mY190aHJlc2hvbGQpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgICAgIGxhYnMoDQogICAgICAgICAgICB4ID0gcGFzdGUoY29udHJhc3RfbGFiLCAiIGxvZzIoRkMpIiksDQogICAgICAgICAgICAgICB5ID0gIlNpZ25pZmljYW5jZSwgLWxvZzEwKFApIiwNCiAgICAgICAgICAgICAgIGNvbG9yID0gIlNpZ25pZmljYW5jZSIpICsNCiAgICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhgRkRSIDwgMC4wMDFgID0gImRvZGdlcmJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBGRFIgPCAwLjA1YCA9ICJsaWdodGJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBQIDwgMC4wNWAgPSAib3JhbmdlMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYE5TIG9yIEZDIDwgMWAgPSAiZ3JheSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsMC4wNSkpKSArDQogICAgICAgICAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBzdWJzZXQobG1tX3Jlc3VsdHNfc2xpY2UsICBgUHIoPnx0fClgIDwgMC4wMDEgJiBhYnMobG1tX3Jlc3VsdHNfc2xpY2UkRXN0aW1hdGUpID4gZmNfdGhyZXNob2xkICYgR2VuZSAlaW4lIHRvcF9nKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAuMTUsIGNvbG9yID0gImJsYWNrIixzaXplPTUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5zZWdtZW50Lmxlbmd0aCA9IC4xLCBib3gucGFkZGluZyA9IC4yLCBsd2QgPSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSA1MCkgKw0KICAgICAgICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSArDQogICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgICAgICAgICBmYWNldF93cmFwKH5TdWJzZXQsIHNjYWxlcyA9ICJmcmVlX3kiKQ0KICBjb3VudGVyIDwtIGNvdW50ZXIgKyAxDQp9DQpncmlkLmFycmFuZ2UoZ3JvYnM9cGxvdHNfbG1tLG5jb2w9MikNCmBgYCAgICANCg0KYGBge3IgdGFibGVfb2ZfTE1NX3Jlc3VsdHMgcmVnaW9ufQ0KI3N1YnNldChsbW1fcmVzdWx0cywgR2VuZSAlaW4lIEdPSSkNCmRhdGF0YWJsZShsbW1fcmVzdWx0cywgcm93bmFtZXMgPSBGQUxTRSwNCiAgICAgICAgICBleHRlbnNpb25zID0gJ0J1dHRvbnMnLCBvcHRpb25zID0gbGlzdCAoDQogICAgICAgICAgICAgIGRvbSA9ICdCZnRyaXAnLA0KICAgICAgICAgICAgICBidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykNCiAgICAgICAgICAgICksDQogICAgICAgICAgY2FwdGlvbiA9ICJERSByZXN1bHRzIGZvciBHZW5lcyBvZiBJbnRlcmVzdCAoPjc1JSBDVikiLGZpbHRlcj0ndG9wJykgJT4lIGZvcm1hdFJvdW5kKGNvbHVtbnM9YygiRXN0aW1hdGUiLCJQcig+fHR8KSIsIkZEUiIpLCBkaWdpdHM9MykNCmBgYA0KDQojIDcuNCBQbG90dGluZyBHZW5lcyBvZiBJbnRlcmVzdA0KDQpgYGB7ciBwbG90X2dlbmVfb2ZfaW50ZXJlc3QsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTV9DQpteV9nb2lzIDwtdW5pcXVlKHN1YnNldChsbW1fcmVzdWx0cywgYEZEUmAgPCAwLjAwMSkkR2VuZSkNCnRtcF90Ymw8LXN1YnNldChsbW1fcmVzdWx0cywgR2VuZSAlaW4lIG15X2dvaXMpDQoNCm15X2dvaXMgPC0gYygpDQpmb3IgKGNvbnRyYXN0IGluIHVuaXF1ZSh0bXBfdGJsJENvbnRyYXN0KSkgeyAjIGdlbmUgb2YgaW50ZXJlc3QgZm9yIGFsbCBjb250cmFzdHMNCiNmb3IgKGNvbnRyYXN0IGluIGMoIkp1eF9HbG8gLSBQcm9fVHViIikpIHsNCiAgaW5kIDwtIHRtcF90YmwkQ29udHJhc3QgPT0gY29udHJhc3QNCiAgZ29pIDwtIHRtcF90YmxbaW5kLCAiR2VuZSJdW29yZGVyKHRtcF90YmxbaW5kLCAiRXN0aW1hdGUiXSwgZGVjcmVhc2luZyA9IEZBTFNFKVsxOjNdXQ0KICBteV9nb2lzIDwtIGMobXlfZ29pcywgZ29pKQ0KICBnb2kgPC0gdG1wX3RibFtpbmQsICJHZW5lIl1bb3JkZXIodG1wX3RibFtpbmQsICJFc3RpbWF0ZSJdLCBkZWNyZWFzaW5nID0gVFJVRSlbMTozXV0NCiAgbXlfZ29pcyA8LSBjKG15X2dvaXMsIGdvaSkNCn0NCg0KaWYobnJvdyh0bXBfdGJsKSA+IDEpIHsgDQogIGRhdGF0YWJsZSh0bXBfdGJsLHJvd25hbWVzID0gRkFMU0UsY2FwdGlvbiA9ICJERSByZXN1bHRzIGZvciBHZW5lcyBvZiBJbnRlcmVzdCAiLGZpbHRlcj0ndG9wJykgJT4lIGZvcm1hdFJvdW5kKGNvbHVtbnM9YygiRXN0aW1hdGUiLCJQcig+fHR8KSIsIkZEUiIpLCBkaWdpdHM9MykNCiANCmZvciAobXlfZ29pIGluIG15X2dvaXMpIHsNCiMgc2hvdyBleHByZXNzaW9uIGZvciBhIHNpbmdsZSB0YXJnZXQNCiAgYTwtZ2dwbG90KHBEYXRhKHRhcmdldF9EYXRhKSwNCiAgICAgICBhZXMoeCA9IEFOTjIsIGZpbGwgPSBBTk4yLA0KICAgICAgICAgICB5ID0gYXMubnVtZXJpYyhhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhW215X2dvaSwgXSwgZWx0ID0gInFfbm9ybSIpKSkpICsNCiAgZ2VvbV92aW9saW4oKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gLjIpICsNCiAgbGFicyh5ID0gcGFzdGUobXlfZ29pLCJFeHByZXNzaW9uIikpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArDQogICNmYWNldF93cmFwKH5BTk4zLCBucm93PTEpICsgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpICsNCiAgdGhlbWVfYncoKSArIGNvb3JkX2ZsaXAoKQ0KICBwcmludChhKQ0KfQ0KfWVsc2V7DQogIHByaW50KCJObyBzaWduaWZpY2FudCBsTU0gcmVzdWx0cyB0byBwbG90IikNCn0NCmBgYA0KDQojIDcuNSBIZWF0bWFwIG9mIFNpZ25pZmljYW50IEdlbmVzDQoNCkluIGFkZGl0aW9uIHRvIGdlbmVyYXRpbmcgaW5kaXZpZHVhbCBnZW5lIGJveCBwbG90cyBvciB2b2xjYW5vIHBsb3RzLCB3ZQ0KY2FuIGFnYWluIGNyZWF0ZSBhIGhlYXRtYXAgZnJvbSBvdXIgZGF0YS4gVGhpcyB0aW1lIHJhdGhlciB0aGFuDQp1dGlsaXppbmcgQ1YgdG8gc2VsZWN0IGdlbmVzLCB3ZSBjYW4gdXNlIHRoZSBQLXZhbHVlIG9yIEZEUiB2YWx1ZXMgdG8NCnNlbGVjdCBnZW5lcy4gSGVyZSwgd2UgcGxvdCBhbGwgZ2VuZXMgd2l0aCBhbiBGRFIgXDwgMC4wMDEuDQoNCmBgYHtyIGhlYXRtYXBfc2lnX2dlbmVzLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0yMH0NCm15X2dvaXMgPC11bmlxdWUoc3Vic2V0KGxtbV9yZXN1bHRzLCBgRkRSYCA8IDAuMDAxKSRHZW5lKSAgICMgMTAwIHRvIHByZXZlbnQgbG9uZyBydW50aW1lDQoNCmlmKGxlbmd0aChteV9nb2lzKTwyKSB7DQogIHByaW50KCJObyBzaWduaWZpY2FudCByZXN1bHRzIHRvIHNob3ciKQ0KIA0KfWVsc2V7DQoNCnBoZWF0bWFwKGxvZzIoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVtteV9nb2lzLCBdLCBlbHQgPSAicV9ub3JtIikpLA0KICAgICAgICAgc2NhbGUgPSAicm93IiwNCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBUUlVFLCBzaG93X2NvbG5hbWVzID0gVFJVRSwNCiAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLA0KICAgICAgICAgY2x1c3RlcmluZ19tZXRob2QgPSAiYXZlcmFnZSIsDQogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgIGN1dHJlZV9jb2xzID0gMywgY3V0cmVlX3Jvd3MgPSAyLA0KICAgICAgICAgYnJlYWtzID0gc2VxKC0zLCAzLCAwLjA1KSwNCiAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJwdXJwbGUzIiwgImJsYWNrIiwgInllbGxvdzIiKSkoMTIwKSwNCiAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gY29sb3JfbGlzdCwNCiAgICAgICAgIGFubm90YXRpb25fY29sID0gcERhdGEodGFyZ2V0X0RhdGEpWywgYW5uX25hbWVzXSkNCn0NCmBgYA0KDQojIDggUGF0aHdheSBBbmFseXNpcw0KDQpQYXRod2F5IGFuYWx5c2lzIGVuYWJsZXMgZXhwbG9yYXRpb24gb2YgZGlmZmVyZW50IGFnZ3JlZ2F0ZSBnZW5lIHNldHMNCmZvciBvdXIgZXhwZXJpbWVudGFsIHF1ZXN0aW9ucy4gRWFjaCBpbmRpdmlkdWFsIFJPSS9BT0kgc2VnbWVudCBpcw0Kc2NvcmVkIGZvciBldmVyeSBwYXRod2F5IG9mIGludGVyZXN0LCB3aGljaCB3ZSBjYW4gdGhlbiB1c2UgdG8NCmludmVzdGlnYXRlIGJpb2xvZ2ljYWwgZGlmZmVyZW5jZXMuIFdlIHdpbGwgcGVyZm9ybSBhbmFsb2dvdXMgYW5hbHlzZXMNCmFzIHRob3NlIG91dGxpbmVkIGluIHRoZSBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBhbmQgU3BhdGlhbA0KRGVjb252b2x1dGlvbiBzZWN0aW9ucyBvZiB0aGUgcmVwb3J0IGZvciBnZW5lIHNldCBlbnJpY2htZW50Lg0KDQojIDguMSBTY29yaW5nIEdlbmUgU2V0cw0KDQpQYXRod2F5cyBhbmQgZ2VuZSBzZXRzIHdlcmUgZGVmaW5lZCBmcm9tIHRoZSBLZWdnIEJyaXRlIGRhdGFiYXNlLiBXZSB1c2UNCmFuIFIgc29mdHdhcmUgcGFja2FnZSBjYWxsZWQgR2VuZSBTZXQgVmFyaWF0aW9uIEFuYWx5c2lzIHRvIHNjb3JlIGVhY2gNCnNlZ21lbnQgd2l0aGluIG91ciBzdHVkeS4gc2VlDQo8aHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL21zaWdkYnIvdmlnbmV0dGVzL21zaWdkYnItaW50cm8uaHRtbD4NCmZvciBvcHRpb25zIG9uIGNvbGxlY3Rpb25zLiBXZSB1c2UgdGhlIEtFR0cgYW5kIFJFQUNUT01FLg0KDQpgYGB7ciBidWlsZF9nZW5lc2V0c30NCiNnYygpDQpoX2dlbmVfc2V0cyA9IHJiaW5kKG1zaWdkYnIoc3BlY2llcyA9ICJodW1hbiIsIHN1YmNhdGVnb3J5ID0gIkNQOktFR0ciKSwNCiAgICAgICAgICAgICAgICAgICAgbXNpZ2RicihzcGVjaWVzID0gImh1bWFuIiwgc3ViY2F0ZWdvcnkgPSAiQ1A6UkVBQ1RPTUUiKSkNCiNtc2lnZGJyKHNwZWNpZXMgPSAiaHVtYW4iLCBzdWJjYXRlZ29yeSA9ICJDUDpCSU9DQVJUQSIpDQoNCm1zaWdkYnJfbGlzdCA9IHNwbGl0KHggPSBoX2dlbmVfc2V0cyRnZW5lX3N5bWJvbCwgZiA9IGhfZ2VuZV9zZXRzJGdzX25hbWUpDQoNCiMgcHJlcGFyZSBkZiBmb3IgYWNjdXJhdGUgbWVyZ2luZyBiYWNrIGdlbmVzIGxhdGVyDQpwd19nZW5lX2RmPC1kYXRhLmZyYW1lKFBhdGh3YXkgPSBuYW1lcyhtc2lnZGJyX2xpc3QpKQ0KcHdfZ2VuZV9kZiRnZW5lczwtbXNpZ2Ricl9saXN0DQpgYGANCg0KYGBge3IgcnVuX2dzdmF9DQpzc2dzZWFfcmVzdWx0cyA8LSBHU1ZBOjpnc3ZhKGV4cHIgPSBhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsdCA9ICJsb2dfcSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdzZXQuaWR4Lmxpc3QgPSBtc2lnZGJyX2xpc3QsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInNzZ3NlYSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLnN6ID0gNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXguc3ogPSA1MDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKQ0KZ2VuZVNldE9iaiA8LQ0KICBOYW5vU3RyaW5nR2VvTXhTZXQoYXNzYXlEYXRhID0gc3Nnc2VhX3Jlc3VsdHMsDQogICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGEgPSBBbm5vdGF0ZWREYXRhRnJhbWUocERhdGEodGFyZ2V0X0RhdGEpKSwNCiAgICAgICAgICAgICAgICAgICAgIHByb3RvY29sRGF0YSA9IHByb3RvY29sRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgICAgICAgICAgICAgICBmZWF0dXJlVHlwZSA9ICJHZW5lU2V0IiwNCiAgICAgICAgICAgICAgICAgICAgIGNoZWNrID0gRkFMU0UpDQpgYGANCg0KIyA4LjIgRGlmZmVyZW50YWwgYW5hbHlzaXMgb2YgcGF0aHdheXMNCg0KYGBge3IgTE1NX29mX3BhdGh3YXlfYW5hbGlzaXMgcmVnaW9ufQ0KIyAjIGNvbnZlcnQgdGVzdCB2YXJpYWJsZXMgdG8gZmFjdG9ycw0KcERhdGEodGFyZ2V0X0RhdGEpJHRlc3RDbGFzcyA8LSBmYWN0b3IocERhdGEodGFyZ2V0X0RhdGEpJEFOTjEsIHVuaXF1ZSh0YXJnZXRfRGF0YSRBTk4xKSkNCnBEYXRhKGdlbmVTZXRPYmopW1sic2xpZGUiXV08LWZhY3RvcihwRGF0YShnZW5lU2V0T2JqKVtbInNsaWRlX25hbWUiXV0pDQojcERhdGEodGFyZ2V0X0RhdGEpJHRlc3RSZWdpb248LWZhY3RvcihwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMiwgdW5pcXVlKGNvdW50X21hdCRBTk4zKSkNCg0KbG1tX3NzZ3NlYV9yZXN1bHRzIDwtIGMoKQ0KZm9yIChyZWdpb24gaW4gdW5pcXVlKHBEYXRhKHRhcmdldF9EYXRhKSRBTk4zKSkgew0KICBpbmQgPC0gcERhdGEodGFyZ2V0X0RhdGEpJEFOTjMgPT0gcmVnaW9uDQogIG1peGVkT3V0bWMgPC0NCiAgICBtaXhlZE1vZGVsREUoZ2VuZVNldE9ialssIGluZF0sDQogICAgICAgICAgICAgICAgIGVsdCA9ICJleHBycyIsDQogICAgICAgICAgICAgICAgIG1vZGVsRm9ybXVsYSA9IH4gdGVzdENsYXNzICsgKDEgfCBzbGlkZSksDQogICAgICAgICAgICAgICAgIGdyb3VwVmFyID0gInRlc3RDbGFzcyIsDQogICAgICAgICAgICAgICAgIG5Db3JlcyA9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpLA0KICAgICAgICAgICAgICAgICBtdWx0aUNvcmUgPSBUUlVFKQ0KDQogICMgZm9ybWF0IHJlc3VsdHMgYXMgZGF0YS5mcmFtZQ0KICByX3NzZ3NlYV90ZXN0IDwtIGRvLmNhbGwocmJpbmQsIG1peGVkT3V0bWNbImxzbWVhbnMiLCBdKQ0KICBzc2dzZWFfdGVzdHMgPC0gcm93bmFtZXMocl9zc2dzZWFfdGVzdCkNCiAgcl9zc2dzZWFfdGVzdCA8LSBhcy5kYXRhLmZyYW1lKHJfc3Nnc2VhX3Rlc3QpDQogIHJfc3Nnc2VhX3Rlc3QkQ29udHJhc3QgPC0gc3Nnc2VhX3Rlc3RzDQogICNyX3NzZ3NlYV90ZXN0JEdlbmVzIDwtIG1zaWdkYnJfbGlzdCBzZWVtcyB1bnJlbGlhYmxlIGFzIGdzdmEgb21pdHMgcGF0aHdheXMgaWYgZ2VuZXMgYXJlIG5vdCBpbiBkYXRhLi4NCg0KICAjIHVzZSBsYXBwbHkgaW4gY2FzZSB5b3UgaGF2ZSBtdWx0aXBsZSBsZXZlbHMgb2YgeW91ciB0ZXN0IGZhY3RvciB0bw0KICAjIGNvcnJlY3RseSBhc3NvY2lhdGUgZ2VuZSBuYW1lIHdpdGggaXQncyByb3cgaW4gdGhlIHJlc3VsdHMgdGFibGUNCiAgcl9zc2dzZWFfdGVzdCRQYXRod2F5IDwtDQogICAgdW5saXN0KGxhcHBseShjb2xuYW1lcyhtaXhlZE91dG1jKSwNCiAgICAgICAgICAgICAgICAgIHJlcCwgbnJvdyhtaXhlZE91dG1jWyJsc21lYW5zIiwgXVtbMV1dKSkpDQoNCiAgI3Jfc3Nnc2VhX3Rlc3QkU3Vic2V0IDwtIHN0YXR1cw0KICByX3NzZ3NlYV90ZXN0JEZEUiA8LSBwLmFkanVzdChyX3NzZ3NlYV90ZXN0JGBQcig+fHR8KWAsIG1ldGhvZCA9ICJmZHIiKQ0KICByX3NzZ3NlYV90ZXN0JFN1YnNldCA8LSByZWdpb24NCiAgcl9zc2dzZWFfdGVzdCA8LSByX3NzZ3NlYV90ZXN0WywgYygiUGF0aHdheSIsICJTdWJzZXQiLCAiQ29udHJhc3QiLCAiRXN0aW1hdGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQcig+fHR8KSIsICJGRFIiKV0NCiAgbG1tX3NzZ3NlYV9yZXN1bHRzIDwtIHJiaW5kKGxtbV9zc2dzZWFfcmVzdWx0cywgcl9zc2dzZWFfdGVzdCkNCg0KICB9DQogIGxtbV9zc2dzZWFfcmVzdWx0cyA8LSBtZXJnZShsbW1fc3Nnc2VhX3Jlc3VsdHMsIHB3X2dlbmVfZGYpDQpgYGANCg0KIyA4LjIuMSBUYWJsZSBvZiBEaWZmZXJlbnRhbCBhbmFseXNpcyBvZiBwYXRod2F5cw0KDQpgYGB7ciB0YWJsZV9vZl9MTU1fcGF0aHdheV9yZXN1bHRzIHJlZ2lvbiwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MjB9DQpkYXRhdGFibGUoc3Vic2V0KGxtbV9zc2dzZWFfcmVzdWx0cyksIHJvd25hbWVzID0gRkFMU0UsDQogICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgIHBhZ2VMZW5ndGggPSAxMCwNCiAgICAgICAgICAgICAgZG9tID0gJ0JmdHJpcCcsDQogICAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKQ0KICAgICAgICAgICAgKSwNCiAgICAgICAgICBjYXB0aW9uID0gIkRFIHJlc3VsdHMgZm9yIFBhdGh3YXlzIixmaWx0ZXI9J3RvcCcpICU+JSBmb3JtYXRSb3VuZChjb2x1bW5zPWMoIkVzdGltYXRlIiwiUHIoPnx0fCkiLCJGRFIiKSwgZGlnaXRzPTUpDQpgYGANCg0KIyA4LjMgVnVsY2Fub3Bsb3Qgb2YgTE1NX1BhdGh3YXlzDQoNCmBgYHtyIGxtbV9wd192dWxjYW5vLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMH0NCiMgQ2F0ZWdvcml6ZSBSZXN1bHRzIGJhc2VkIG9uIFAtdmFsdWUgJiBGRFIgZm9yIHBsb3R0aW5nDQpmY190aHJlc2hvbGQgPSAwLjMNCg0KbG1tX3NzZ3NlYV9yZXN1bHRzJENvbG9yIDwtICJOUyBvciBGQyA8IDAuMyINCmxtbV9zc2dzZWFfcmVzdWx0cyRDb2xvcltsbW1fc3Nnc2VhX3Jlc3VsdHMkYFByKD58dHwpYCA8IDAuMDVdIDwtICJQIDwgMC4wNSINCmxtbV9zc2dzZWFfcmVzdWx0cyRDb2xvcltsbW1fc3Nnc2VhX3Jlc3VsdHMkRkRSIDwgMC4wNV0gPC0gIkZEUiA8IDAuMDUiDQpsbW1fc3Nnc2VhX3Jlc3VsdHMkQ29sb3JbbG1tX3NzZ3NlYV9yZXN1bHRzJEZEUiA8IDAuMDAxXSA8LSAiRkRSIDwgMC4wMDEiDQpsbW1fc3Nnc2VhX3Jlc3VsdHMkQ29sb3JbYWJzKGxtbV9zc2dzZWFfcmVzdWx0cyRFc3RpbWF0ZSkgPCBmY190aHJlc2hvbGRdIDwtICJOUyBvciBGQyA8IDAuMyINCmxtbV9zc2dzZWFfcmVzdWx0cyRDb2xvciA8LSBmYWN0b3IobG1tX3NzZ3NlYV9yZXN1bHRzJENvbG9yLA0KICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiTlMgb3IgRkMgPCAwLjMiLCAiUCA8IDAuMDUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRkRSIDwgMC4wNSIsICJGRFIgPCAwLjAwMSIpKQ0KDQojIHBpY2sgdG9wIHB3IGZvciBlaXRoZXIgc2lkZSBvZiB2b2xjYW5vIHRvIGxhYmVsDQojIG9yZGVyIHB3IGZvciBjb252ZW5pZW5jZToNCmxtbV9zc2dzZWFfcmVzdWx0cyRpbnZlcnRfUCA8LSAoLWxvZzEwKGxtbV9zc2dzZWFfcmVzdWx0cyRgUHIoPnx0fClgKSkgKiBzaWduKGxtbV9zc2dzZWFfcmVzdWx0cyRFc3RpbWF0ZSkNCnRvcF9zc2dzZWFfZyA8LSBjKCkNCg0KI2xvb3AgaGVyZSBvdmVyIHRlc3RlZCBjb25kaXRpb25zIGlmIGFwcGxpY2FibGUNCg0KZm9yKGMgaW4gdW5pcXVlKGxtbV9zc2dzZWFfcmVzdWx0cyRTdWJzZXQpICkgew0KICBsbW1fcmVzdWx0c19zbGljZSA9IGxtbV9zc2dzZWFfcmVzdWx0c1tsbW1fc3Nnc2VhX3Jlc3VsdHMkU3Vic2V0ID09IGMsXQ0KIHRvcF9zc2dzZWFfZyA8LSBjKHRvcF9zc2dzZWFfZywNCiAgICAgICAgIGxtbV9zc2dzZWFfcmVzdWx0c1tpbmQsICdQYXRod2F5J11bDQogICAgICAgICAgICAgICBvcmRlcihsbW1fc3Nnc2VhX3Jlc3VsdHNbaW5kLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MTVdXSwNCiAgICAgICAgIGxtbV9zc2dzZWFfcmVzdWx0c1tpbmQsICdQYXRod2F5J11bDQogICAgICAgICAgICAgICBvcmRlcihsbW1fc3Nnc2VhX3Jlc3VsdHNbaW5kLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IEZBTFNFKVsxOjE1XV0pDQp9DQogDQp0b3Bfc3Nnc2VhX2cgPC0gdW5pcXVlKHRvcF9zc2dzZWFfZykNCmxtbV9zc2dzZWFfcmVzdWx0cyA8LSBsbW1fc3Nnc2VhX3Jlc3VsdHNbLCAtMSpuY29sKGxtbV9zc2dzZWFfcmVzdWx0cyldICMgcmVtb3ZlIGludmVydF9QIGZyb20gbWF0cml4DQoNCiNsbW1fc3Nnc2VhX3Jlc3VsdHNfc2xpY2UgPC0gbG1tX3NzZ3NlYV9yZXN1bHRzX3NsaWNlW2xtbV9zc2dzZWFfcmVzdWx0c19zbGljZSRGRFIgPCAxLF0NCg0KIyBHcmFwaCByZXN1bHRzDQpnZ3Bsb3QobG1tX3NzZ3NlYV9yZXN1bHRzLA0KICAgICAgIGFlcyh4ID0gRXN0aW1hdGUsIHkgPSAtbG9nMTAoYFByKD58dHwpYCksDQogICAgICAgICAgIGNvbG9yID0gQ29sb3IsIGxhYmVsID0gUGF0aHdheSkpICsNCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKDAuMiwgLTAuMiksIGx0eSA9ICJkYXNoZWQiKSArDQogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgbGFicyh4ID0gcGFzdGUobG1tX3Jlc3VsdHNfc2xpY2UkQ29udHJhc3QsICJsb2cyKEZDKSIpLA0KICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICBjb2xvciA9ICJTaWduaWZpY2FuY2UiKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoYEZEUiA8IDAuMDAxYCA9ICJkb2RnZXJibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgUCA8IDAuMDVgID0gIm9yYW5nZTIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBOUyBvciBGQyA8IDAuNWAgPSAiZ3JheSIpLA0KICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsMC4wNSkpKSArDQogICAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBzdWJzZXQobG1tX3NzZ3NlYV9yZXN1bHRzLCBDb2xvciA9PSAiRkRSIDwgMC4wNSIgfCBDb2xvciA9PSAiRkRSIDwgMC4wMDEiKSwNCiAgICAgICAgICAgICAgICAgICBwb2ludC5wYWRkaW5nID0gMC4xNSwgY29sb3IgPSAiYmxhY2siLHNpemU9MywNCiAgICAgICAgICAgICAgICAgICBtaW4uc2VnbWVudC5sZW5ndGggPSAuMSwgYm94LnBhZGRpbmcgPSAuMiwgbHdkID0gMiwNCiAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSA1MCkgKw0KICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgICBmYWNldF93cmFwKH5TdWJzZXQsIHNjYWxlcyA9ICJmcmVlX3kiKQ0KDQojICAgICtmYWNldF93cmFwKH5TdWJzZXQsIHNjYWxlcyA9ICJmcmVlX3kiKSkNCmBgYA0KDQojIDguNCBoZWF0bWFwIG9mIHBhdGh3YXlzDQoNCmBgYHtyIHB3X2hlYXRtYXAsIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTIwfQ0KICBhY3RpdmVfcHc8LWZpbHRlcihsbW1fc3Nnc2VhX3Jlc3VsdHMsIGBQcig+fHR8KWAgPCAwLjAwMSkkUGF0aHdheQ0KICBhY3RpdmVfcHc8LWZpbHRlcihsbW1fc3Nnc2VhX3Jlc3VsdHMsIEZEUiA8IDAuMDAxICkkUGF0aHdheQ0KICAjYWN0aXZlX3B3PC1maWx0ZXIobG1tX3NzZ3NlYV9yZXN1bHRzLCBDb2xvciA9PSAiRkRSIDwgMC4wMDEiKSRQYXRod2F5DQogIA0KICANCiAgYWN0aXZlX3B3PC10b3Bfc3Nnc2VhX2cNCiAgDQogIGlmIChsZW5ndGgoYWN0aXZlX3B3KT4xKSB7DQogIA0KICAgIHByaW50KCJnbyIpDQogICAgDQogIHB3X21hdHJpeDwtYXNzYXlEYXRhRWxlbWVudChnZW5lU2V0T2JqLCBlbHQgPSAiZXhwcnMiKQ0KDQogIHBoZWF0bWFwKHB3X21hdHJpeFthY3RpdmVfcHcsXSwNCiAgICAgICAgIHNjYWxlID0gInJvdyIsDQogICAgICAgICBzaG93X3Jvd25hbWVzID0gVFJVRSwgc2hvd19jb2xuYW1lcyA9IFRSVUUsDQogICAgICAgICBmb250c2l6ZV9yb3cgPSAxMCwNCiAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLA0KICAgICAgICAgY2x1c3RlcmluZ19tZXRob2QgPSAiYXZlcmFnZSIsDQogICAgICAgICAjY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJldWNsaWRlYW4iLA0KICAgICAgICAgY3V0cmVlX2NvbHMgPSAyLCBjdXRyZWVfcm93cyA9IDIsDQogICAgICAgICBicmVha3MgPSBzZXEoLTMsIDMsIDAuMDUpLA0KICAgICAgICAgI2NvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJwdXJwbGUzIiwgImJsYWNrIiwgInllbGxvdzIiKSkoMTIwKSwNCiAgICAgICAgIG1haW4gPSAiSGVhdG1hcCBvZiBzZWxlY3RlZCBQYXRod2F5cyIsDQogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGNvbG9yX2xpc3QsDQogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IHBEYXRhKHRhcmdldF9EYXRhKVssIGFubl9uYW1lc10pDQogIA0KICB9ZWxzZXsNCiAgICBwcmludCgiTm8gc2lnbmlmaWNhbnQgcmVzdWx0cyB0byBkaXNwbGF5IikNCiAgfQ0KYGBgDQoNCiMgOC41IGZnc2VhIHBhdGh3YXkgYW5hbHlzaXMNCg0KQW5vdGhlciBvcHRpb24gZm9yIHBhdGh3YXkgYW5hbHlzaXMgaXMgdGhlIFIgc29mdHdhcmUgcGFja2FnZSBjYWxsZWQgRmFzdCBHZW5lIFNldCBFbnJpY2htZW50IEFuYWx5c2lzLiBJdCBpcyBhIHByZS1yYW5rZWQgbWV0aG9kIHRoYXQgdXNlcyB0aGUgTE1NIHJlc3VsdHMgY29tYmluZWQgd2l0aCB0aGUgc2FtZSBwYXRod2F5IGxpc3QgZnJvbSBtc2lnZGJyIGFzIEdTVkEuDQoNCmBgYHtyIGZnc2VhfQ0KZmdzZWFfcmVzdWx0c19hbGwgPC0gYygpDQpmb3IgKGNvbnRyYXN0IGluIHVuaXF1ZShsbW1fcmVzdWx0cyRDb250cmFzdCkpIHsNCiAgICANCiAgICByYW5rcyA8LSBsbW1fcmVzdWx0cyRFc3RpbWF0ZVtsbW1fcmVzdWx0cyRDb250cmFzdCA9PSBjb250cmFzdF0NCiAgICBuYW1lcyhyYW5rcykgPC0gbG1tX3Jlc3VsdHMkR2VuZVtsbW1fcmVzdWx0cyRDb250cmFzdCA9PSBjb250cmFzdF0NCiAgICANCiAgICBmZ3NlYV9yZXN1bHRzIDwtIGZnc2VhKG1zaWdkYnJfbGlzdCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgcmFua3MsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGVwcyA9IDAuMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBtaW5TaXplPTE1LCANCiAgICAgICAgICAgICAgICAgICAgICAgICBtYXhTaXplID0gNTAwKQ0KICAgIGZnc2VhX3Jlc3VsdHMkQ29udHJhc3QgPC0gY29udHJhc3QNCiAgICBmZ3NlYV9yZXN1bHRzX2FsbCA8LSByYmluZChmZ3NlYV9yZXN1bHRzX2FsbCwgZmdzZWFfcmVzdWx0cykNCiAgICANCn0NCg0KZmdzZWFfcmVhY3RvbWUgPC0gZmdzZWFfcmVzdWx0c19hbGxbZ3JlcCgiUkVBQ1RPTUUiLCBwYXRod2F5KSxdDQpmZ3NlYV9yZWFjdG9tZSRwYXRod2F5IDwtIGdzdWIoIlJFQUNUT01FXyIsICIiLCBmZ3NlYV9yZWFjdG9tZSRwYXRod2F5KQ0KZmdzZWFfcmVhY3RvbWUkcGF0aHdheSA8LSBnc3ViKCJfIiwgIiAiLCBmZ3NlYV9yZWFjdG9tZSRwYXRod2F5KQ0KYGBgDQoNCmBgYHtyIGZnc2VhX3B3X3Z1bGNhbm8sIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTEwfQ0KIyBDYXRlZ29yaXplIFJlc3VsdHMgYmFzZWQgb24gUC12YWx1ZSAmIEZEUiBmb3IgcGxvdHRpbmcNCmZjX3RocmVzaG9sZCA9IDAuMw0KDQoNCmZnc2VhX3Jlc3VsdHNfYWxsJENvbG9yIDwtICJOUyBvciBGQyA8IDAuMyINCmZnc2VhX3Jlc3VsdHNfYWxsJENvbG9yW2Znc2VhX3Jlc3VsdHNfYWxsJHB2YWwgPCAwLjA1XSA8LSAiUCA8IDAuMDUiDQpmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvcltmZ3NlYV9yZXN1bHRzX2FsbCRwYWRqIDwgMC4wNV0gPC0gIkZEUiA8IDAuMDUiDQpmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvcltmZ3NlYV9yZXN1bHRzX2FsbCRwYWRqIDwgMC4wMDFdIDwtICJGRFIgPCAwLjAwMSINCmZnc2VhX3Jlc3VsdHNfYWxsJENvbG9yW2FicyhmZ3NlYV9yZXN1bHRzX2FsbCRFUykgPCBmY190aHJlc2hvbGRdIDwtICJOUyBvciBGQyA8IDAuMyINCmZnc2VhX3Jlc3VsdHNfYWxsJENvbG9yIDwtIGZhY3RvcihmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvciwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk5TIG9yIEZDIDwgMC4zIiwgIlAgPCAwLjA1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZEUiA8IDAuMDUiLCAiRkRSIDwgMC4wMDEiKSkNCg0KcGx0IDwtIGh0bWx0b29sczo6dGFnTGlzdCgpDQpjb3VudGVyID0gMQ0KZm9yKGMgaW4gdW5pcXVlKGZnc2VhX3Jlc3VsdHNfYWxsJENvbnRyYXN0KSApIHsNCiAgZmdzZWFfcmVzdWx0c19zbGljZSA9IGZnc2VhX3Jlc3VsdHNfYWxsW2Znc2VhX3Jlc3VsdHNfYWxsJENvbnRyYXN0ID09IGMsXQ0KDQogICMgIyBwaWNrIHRvcCBnZW5lcyBmb3IgZWl0aGVyIHNpZGUgb2Ygdm9sY2FubyB0byBsYWJlbA0KICAjICMgb3JkZXIgZ2VuZXMgZm9yIGNvbnZlbmllbmNlOg0KICBmZ3NlYV9yZXN1bHRzX3NsaWNlJGludmVydF9QIDwtICgtbG9nMTAoZmdzZWFfcmVzdWx0c19zbGljZSRwdmFsKSkgKiBzaWduKGZnc2VhX3Jlc3VsdHNfc2xpY2UkRVMpDQogIA0KICAjICNsb29wIGhlcmUgb3ZlciB0ZXN0ZWQgY29uZGl0aW9ucyBpZiBhcHBsaWNhYmxlDQogICN0b3BfZmdzZWFfZyA8LSBjKCkNCiAgdG9wX2Znc2VhX2cgPC0gYyhmZ3NlYV9yZXN1bHRzX3NsaWNlWywgJ3BhdGh3YXknXVsNCiAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyKGZnc2VhX3Jlc3VsdHNfc2xpY2VbLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MTVdXSwNCiAgICAgICAgICAgICAgICAgICAgZmdzZWFfcmVzdWx0c19zbGljZVssICdwYXRod2F5J11bDQogICAgICAgICAgICAgICAgICAgICAgICBvcmRlcihmZ3NlYV9yZXN1bHRzX3NsaWNlWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToxNV1dKQ0KICAgDQogICMgZm9yIChjZWxsdHlwZSBpbiB1bmlxdWUobG1tX3Jlc3VsdHMkU3Vic2V0KSkgew0KICAjICAgICAgdG9wX2Znc2VhX2cgPC0gYyh0b3BfZmdzZWFfZywNCiAgIyAgICAgICAgICAgICAgICAgIGZnc2VhX3Jlc3VsdHNfc2xpY2VbZmdzZWFfcmVzdWx0c19zbGljZSRTdWJzZXQ9PWNlbGx0eXBlLCAncGF0aHdheSddWw0KICAjICAgICAgICAgICAgICAgICAgICAgIG9yZGVyKGZnc2VhX3Jlc3VsdHNfc2xpY2VbLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MjBdXSwNCiAgIyAgICAgICAgICAgICAgICAgIGZnc2VhX3Jlc3VsdHNfc2xpY2VbZmdzZWFfcmVzdWx0c19zbGljZSRTdWJzZXQ9PWNlbGx0eXBlLCAncGF0aHdheSddWw0KICAjICAgICAgICAgICAgICAgICAgICAgIG9yZGVyKGZnc2VhX3Jlc3VsdHNfc2xpY2VbLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IEZBTFNFKVsxOjIwXV0pDQogICMgfQ0KICANCiAgdG9wX2Znc2VhX2cgPC0gdW5pcXVlKHRvcF9mZ3NlYV9nKQ0KICAjZmdzZWFfcmVzdWx0c19zbGljZSA8LSBmZ3NlYV9yZXN1bHRzX3NsaWNlWywgLTEqbmNvbChmZ3NlYV9yZXN1bHRzX3NsaWNlKV0gIyByZW1vdmUgaW52ZXJ0X1AgZnJvbSBtYXRyaXgNCiAgDQogICMgR3JhcGggcmVzdWx0cw0KICBkeW5wbG90IDwtIGdncGxvdChmZ3NlYV9yZXN1bHRzX3NsaWNlLA0KICAgICAgICAgYWVzKHggPSBORVMsIHkgPSAtbG9nMTAocHZhbCksDQogICAgICAgICAgICAgY29sb3IgPSBDb2xvciwgbGFiZWwgPSBwYXRod2F5KSkgKw0KICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYyhmY190aHJlc2hvbGQsLWZjX3RocmVzaG9sZCksIGx0eSA9ICJkYXNoZWQiKSArDQogICAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAoMC4wNSksIGx0eSA9ICJkYXNoZWQiKSArDQogICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgbGFicyh4ID0gcGFzdGUoYywgIiBGQyIpLA0KICAgICAgICAgICB5ID0gIlNpZ25pZmljYW5jZSwgLWxvZzEwKFApIiwNCiAgICAgICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKw0KICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoYEZEUiA8IDAuMDAxYCA9ICJkb2RnZXJibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBGRFIgPCAwLjA1YCA9ICJsaWdodGJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFAgPCAwLjA1YCA9ICJvcmFuZ2UyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBOUyBvciBGQyA8IDAuM2AgPSAiZ3JheSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDQpKSkgKw0KICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgICAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBzdWJzZXQoZmdzZWFfcmVzdWx0c19zbGljZSwgQ29sb3IgPT0gIlAgPCAwLjA1IiB8IENvbG9yID09ICJGRFIgPCAwLjA1IiB8IENvbG9yID09ICJGRFIgPCAwLjAwMSIpLA0KICAgICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAuMTUsIGNvbG9yID0gImJsYWNrIixzaXplPTUsDQogICAgICAgICAgICAgICAgICAgICBtaW4uc2VnbWVudC5sZW5ndGggPSAuMSwgYm94LnBhZGRpbmcgPSAuMiwgbHdkID0gMiwNCiAgICAgICAgICAgICAgICAgICAgIG1heC5vdmVybGFwcyA9IDUwKSArDQogICAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNikgKw0KICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICMrDQogICAgICAjZmFjZXRfd3JhcCh+U3Vic2V0LCBzY2FsZXMgPSAiZnJlZV95IikNCiAgIA0KICBwbHRbW2NvdW50ZXJdXSA8LSBhc193aWRnZXQoZ2dwbG90bHkoZHlucGxvdCkpDQogIGNvdW50ZXIgPC0gY291bnRlciArIDENCiAgI2RhdGF0YWJsZShzdWJzZXQocmVzdWx0cywgR2VuZSAlaW4lIEdPSSksIHJvd25hbWVzPUZBTFNFLGNhcHRpb24gPSBwYXN0ZSgiREUgcmVzdWx0cyAiLCBhY3RpdmVfZ3JvdXAxLCIgdnMgIiwgYWN0aXZlX2dyb3VwMikpDQp9DQpwbHQNCmBgYA0KDQojIDguNiBWaXN1YWxpemUgS0VHRyBwYXRod2F5DQoNCkdldCBwYXRod2F5IHZpc3VhbGl6YXRpb24gZnJvbSBLRUdHLiBjbHVzdGVyUHJvZmlsZXIgZ3JhYnMgdGhlIEtFR0cgcGF0aHdheSBJRHMgYW5kIGNvbWJpbmVkIHdpdGggcGF0aHZpZXcgaXQgY2FuIGRvd25sb2FkIGEgcGF0aHdheSBpbWFnZSBmcm9tIHRoZSBLRUdHIGRhdGFiYXNlLg0KVGhlIExNTSByZXN1bHRzIGFyZSBnaXZlbiB3aXRoIHRoZSBwYXRod2F5IGlkIHRvIHZpc3VhbGl6ZSB1cCBhbmQgZG93biByZWd1bGF0ZWQgZ2VuZXMuDQoNCmBgYHtyfQ0KZ2V0X3dwX2dtdGZpbGUgPC0gZnVuY3Rpb24oKSB7DQogICAgd3B1cmwgPC0gJ2h0dHBzOi8vd2lraXBhdGh3YXlzLWRhdGEud21jbG91ZC5vcmcvY3VycmVudC9nbXQvJw0KICAgIHggPC0gcmVhZExpbmVzKHdwdXJsKQ0KICAgIHkgPC0geFtncmVwKCdcXC5nbXQnLHgpXQ0KICAgIHN1YigiLiood2lraXBhdGh3YXlzLS4qXFwuZ210KS4qIiwgIlxcMSIsICB5W2dyZXAoJ0ZpbGUnLCB5KV0pDQp9DQoNCmdldF93cF9kYXRhIDwtIGZ1bmN0aW9uKG9yZ2FuaXNtKSB7DQogICAgb3JnYW5pc20gPC0gc3ViKCIgIiwgIl8iLCBvcmdhbmlzbSkNCiAgICBnbXRmaWxlIDwtIGdldF93cF9nbXRmaWxlKCkNCiAgICB3cHVybCA8LSAnaHR0cHM6Ly93aWtpcGF0aHdheXMtZGF0YS53bWNsb3VkLm9yZy9jdXJyZW50L2dtdC8nDQogICAgdXJsIDwtIHBhc3RlMCh3cHVybCwNCiAgICAgICAgICAgICAgICAgIGdtdGZpbGVbZ3JlcChvcmdhbmlzbSwgZ210ZmlsZSldKQ0KICAgIGYgPC0gdGVtcGZpbGUoZmlsZWV4dCA9ICIuZ210IikNCiAgICBkbCA8LSBteWRvd25sb2FkKHVybCwgZGVzdGZpbGUgPSBmKQ0KICAgIGlmIChpcy5udWxsKGYpKSB7DQogICAgICAgIG1lc3NhZ2UoImZhaWwgdG8gZG93bmxvYWQgd2lraVBhdGh3YXlzIGRhdGEuLi4iKQ0KICAgICAgICByZXR1cm4oTlVMTCkNCiAgICB9DQogICAgcmVhZC5nbXQud3AoZikNCn0NCmBgYA0KDQoNCmBgYHtyIGdldCBvbmxpbmUgYXZhaWxhYmxlIHBhdGh3YXlzLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMH0NCiMgZW5yaWNoS0VHRyBncmFiIG9ubGluZSBwYXRod2F5cw0KZ2VuZSA8LSAgdGFyZ2V0X0RhdGFAZmVhdHVyZURhdGFAZGF0YVtbIkdlbmVJRCJdXQ0Ka2sgPC0gZW5yaWNoS0VHRyhnZW5lICAgICAgICAgPSBnZW5lLA0KICAgICAgICAgICAgICAgICBvcmdhbmlzbSAgICAgPSAnaHNhJywNCiAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4xKQ0KDQplbnJpY2hLRUdHX3Jlc3VsdHMgPC0ga2tAcmVzdWx0DQoNCiMgTWF0Y2ggY29sdW1uIHZhbHVlcyBmb3IgbGluaw0KIyBmc2dlYSBydW4NCmZnc2VhX3Jlc3VsdHMkRGVzY3JpcHRpb24gPC0gZ3N1YigiS0VHR18iLCAiIiwgZmdzZWFfcmVzdWx0cyRwYXRod2F5KQ0KZmdzZWFfcmVzdWx0cyREZXNjcmlwdGlvbiA8LSBnc3ViKCJfIiwgIiAiLCBmZ3NlYV9yZXN1bHRzJERlc2NyaXB0aW9uKQ0KIyBzc2dlYSBydW4NCiMgbG1tX3NzZ3NlYV9yZXN1bHRzX2QkRGVzY3JpcHRpb24gPC0gZ3N1YigiS0VHR18iLCAiIiwgbG1tX3NzZ3NlYV9yZXN1bHRzX2QkUGF0aHdheSkNCiMgbG1tX3NzZ3NlYV9yZXN1bHRzX2QkRGVzY3JpcHRpb24gPC0gZ3N1YigiXyIsICIgIiwgbG1tX3NzZ3NlYV9yZXN1bHRzX2QkRGVzY3JpcHRpb24pDQoNCmVucmljaEtFR0dfcmVzdWx0cyREZXNjcmlwdGlvbiA8LSB0b3VwcGVyKGVucmljaEtFR0dfcmVzdWx0cyREZXNjcmlwdGlvbikNCg0KIyBDcmVhdGUgZGYgZm9yIG1hdGNoaW5nIEtFR0cgcmVzdWx0cw0KS0VHR0lEcyA8LSBmZ3NlYV9yZXN1bHRzI1tmZ3NlYV9yZXN1bHRzJENvbnRyYXN0ID09ICJQcm9fVHViIC0gRGlzX1R1YiJdDQpLRUdHSURzIDwtIEtFR0dJRHMgJT4lIGlubmVyX2pvaW4oZW5yaWNoS0VHR19yZXN1bHRzLCBieSA9ICdEZXNjcmlwdGlvbicpICU+JSBzZWxlY3QoRGVzY3JpcHRpb24sIElELCBnZW5lSUQsIGxlYWRpbmdFZGdlLCBwYWRqKQ0KDQojIENyZWF0ZSBnZW5lTGlzdCB3aXRoIERFIHJlc3VsdHMgZm9yIHZpc3VhbGl6YXRpb24gcGF0aHdheSBnZW5lcyANCmdlbmVsaXN0IDwtIGxtbV9yZXN1bHRzJEVzdGltYXRlDQpuYW1lcyhnZW5lbGlzdCkgPC0gdGFyZ2V0X0RhdGFAZmVhdHVyZURhdGFAZGF0YVtbIkdlbmVJRCJdXQ0KDQojIENob29zZSBtYW51YWwgb3IgaGlnaGVzdCBzaWduaWZpY2FudCBwYXRod2F5DQpwYXRod2F5X25hbWUgPC0gIklOU1VMSU4gU0lHTkFMSU5HIFBBVEhXQVkiICMgbWFudWFsIHBhdGh3YXkgcXVlcnkNCnBhdGh3YXlfbmFtZSA8LSB0b3VwcGVyKHBhdGh3YXlfbmFtZSkNCmlmIChwYXRod2F5X25hbWUgPT0gIiIpIHsNCiAgcGF0aHdheV9uYW1lIDwtIEtFR0dJRHNbb3JkZXIoS0VHR0lEcyRwYWRqLCBkZWNyZWFzaW5nID0gRkFMU0UpLCBdWzFdJERlc2NyaXB0aW9uDQp9DQpwYXRod2F5aWQgPC0gS0VHR0lEcyRJRFtLRUdHSURzJERlc2NyaXB0aW9uID09IHBhdGh3YXlfbmFtZV0NCg0KIyBHcmFiIHBhdGh3YXkgaW1hZ2Ugd2l0aCBnZW5lIGluZm8sIHNhdmUgYW5kIHBsb3QNCnZpZXdQYXRoIDwtIHBhdGh2aWV3KGdlbmUuZGF0YSAgPSBnZW5lbGlzdCwNCiAgICAgICAgICAgICAgICAgICAgIHBhdGh3YXkuaWQgPSBwYXRod2F5aWQsDQogICAgICAgICAgICAgICAgICAgICBzcGVjaWVzICAgID0gImhzYSIsDQogICAgICAgICAgICAgICAgICAgICBvdXQuc3VmZml4ID0gImdlbmVzaW5mbyIsIGtlZ2cubmF0aXZlID0gVCwgc2FtZS5sYXllciA9IEYpDQppbWcgPC0gcmVhZFBORyhwYXN0ZShwYXRod2F5aWQsICIuZ2VuZXNpbmZvLnBuZyIsIHNlcCA9ICIiKSkNCmdyaWQ6OmdyaWQucmFzdGVyKGltZykNCmBgYA0KDQojIyBCYXJwbG90IHBhdGh3YXlzDQoNCkNsZWFyIHZpenVhbGl6YXRpb24gb2YgdGhlIHRvcCAxNSBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgZXhwcmVzc2VkIFJlYWN0b21lIHBhdGh3YXlzLiBQYXRod2F5cyB3aXRoIGEgYWRqdXN0ZWQgcC12YWx1ZSBvZiAwLjA1IG9yIGhpZ2hlciB3aWxsIGJlIHByZXNlbnRlZCBhcyByZWQgDQphbmQgYmVsb3cgMC4wNSBhcyBncmVlbi4NCg0KYGBge3IgaG9yaXpvbnRhbCBwYXRod2F5IGJhciBwbG90LCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0yMH0NCnRvcF9hbGwgPC0gYygpDQpmb3IgKGNvbnRyYXN0IGluIHVuaXF1ZShmZ3NlYV9yZWFjdG9tZSRDb250cmFzdCkpIHsNCiAgICAjIGFjdGl2ZV9ncm91cDEgPC0gY29udHJhc3RfbGlzdFtjb250cmFzdF1bWzFdXVtbMV1dDQogICAgIyBhY3RpdmVfZ3JvdXAyIDwtIGNvbnRyYXN0X2xpc3RbY29udHJhc3RdW1sxXV1bWzJdXQ0KICAgICMgaW5kIDwtIHBEYXRhKHRhcmdldF9EYXRhKVtwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMiA9PSBhY3RpdmVfZ3JvdXAxIHwgcERhdGEodGFyZ2V0X0RhdGEpJEFOTjIgPT0gYWN0aXZlX2dyb3VwMl0NCiAgICB0b3AgPC0gZmdzZWFfcmVhY3RvbWVbZmdzZWFfcmVhY3RvbWUkQ29udHJhc3QgPT0gY29udHJhc3RdDQogICAgdG9wcG9zIDwtIHRvcFtvcmRlcih0b3AkTkVTLCBkZWNyZWFzaW5nID0gVFJVRSldWzA6MTVdDQogICAgdG9wbmVnIDwtIHRvcFtvcmRlcih0b3AkTkVTLCBkZWNyZWFzaW5nID0gRkFMU0UpXVswOjE1XQ0KICAgIHRvcCA8LSByYmluZCh0b3Bwb3MsIHRvcG5lZykNCiAgICANCiAgICB0b3AkQ29sb3JbdG9wJHBhZGogPCAwLjA1XSA8LSAicGFkaiA8IDAuMDUiDQogICAgdG9wJENvbG9yW3RvcCRwYWRqID09IDAuMDUgfCB0b3AkcGFkaiA+IDAuMDVdIDwtICJwYWRqID4gMC4wNSINCiAgICB0b3BfYWxsIDwtIHJiaW5kKHRvcF9hbGwsIHRvcCkNCiAgICBiYXJwbG90IDwtIGdncGxvdCh0b3AsYWVzKHg9cmVvcmRlcihwYXRod2F5LCBORVMpLHk9TkVTLGZpbGw9Q29sb3IpKSArIA0KICAgICAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDEwMCkpICsgDQogICAgICBnZW9tX2NvbChwb3NpdGlvbj0iZG9kZ2UiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMyRUNDNzEiLCAiI0U3NEMzQyIpKSArDQogICAgICBnZ3RpdGxlKHBhc3RlKCJUb3AgMTUgKyBhbmQgLSBORVMgcmVhY3RvbWUgcGF0aHdheXMgZnJvbSIsIGNvbnRyYXN0KSkgKyANCiAgICAgIHhsYWIoInBhdGh3YXkiKSArIHlsYWIoIk5vcm1hbGl6ZWQgRW5yaWNobWVudCBTY29yZSIpICsNCiAgICAgIGNvb3JkX2ZsaXAoKSArIA0KICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArDQogICAgICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQ0KICAgICAgI2ZhY2V0X3dyYXAofkNvbnRyYXN0LCBzY2FsZXMgPSAiZnJlZV95IikNCiAgICBwcmludChiYXJwbG90KQ0KfQ0KYGBgDQoNCiMgOSBTcGF0aWFsIERlY29udm9sdXRpb24NCg0KIyMgOS4xIENhbGN1bGF0ZSBiYWNrZ3JvdW5kcw0KDQpgYGB7ciBzcGF0aWFsX2RlY29uX2JnfQ0KI2djKCkNCmJnID0gZGVyaXZlX0dlb014X2JhY2tncm91bmQoDQogIG5vcm0gPSBhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhICwgZWx0ID0gInFfbm9ybSIpLA0KICBwcm9iZXBvb2wgPSBmRGF0YSh0YXJnZXRfRGF0YSkkTW9kdWxlLA0KICBuZWduYW1lcyA9IGMoIk5lZ1Byb2JlLUNUUDAxIiwiTmVnUHJvYmUtS2lsbyIsIk5lZ2F0aXZlIFByb2JlIiwgIk5lZ1Byb2JlLVdUWCIgKSkNCiAgI25lZ25hbWVzID0gIk5lZ1Byb2JlLVdUWCIpDQoNCmBgYA0KDQojIyA5LjIgTG9hZCBjZWxsIHByb2ZpbGUNCg0KQSAiY2VsbCBwcm9maWxlIG1hdHJpeCIgaXMgYSBwcmUtZGVmaW5lZCBtYXRyaXggdGhhdCBzcGVjaWZpZXMgdGhlDQpleHBlY3RlZCBleHByZXNzaW9uIHByb2ZpbGVzIG9mIGVhY2ggY2VsbCB0eXBlIGluIHRoZSBleHBlcmltZW50LiBUaGUNClNwYXRpYWxEZWNvbiBsaWJyYXJ5IGNvbWVzIHdpdGggb25lIHN1Y2ggbWF0cml4IHByZS1sb2FkZWQsIHRoZQ0KIlNhZmVUTUUiIG1hdHJpeCwgZGVzaWduZWQgZm9yIGVzdGltYXRpb24gb2YgaW1tdW5lIGFuZCBzdHJvbWEgY2VsbHMgaW4NCnRoZSB0dW1vciBtaWNyb2Vudmlyb25tZW50LiAoVGhpcyBtYXRyaXggd2FzIGRlc2lnbmVkIHRvIGF2b2lkIGdlbmVzDQpjb21tb25seSBleHByZXNzZWQgYnkgY2FuY2VyIGNlbGxzOyBzZWUgdGhlIFNwYXRpYWxEZWNvbiBtYW51c2NyaXB0IGZvcg0KZGV0YWlscy4pLiBPdGhlcndpc2UsIGxvYWQgc3BlY2lmaWMgcHJvZmlsZXMgZnJvbQ0KPGh0dHBzOi8vZ2l0aHViLmNvbS9OYW5vc3RyaW5nLUJpb3N0YXRzL0NlbGxQcm9maWxlTGlicmFyeS90cmVlL05ld1Byb2ZpbGVNYXRyaWNlcz4NCg0KYGBge3IgbG9hZF9jZWxsX3Byb2ZpbGVzfQ0KI3NhZmVUTUUNCmRhdGEoInNhZmVUTUUiKQ0KZGF0YSgic2FmZVRNRS5tYXRjaGVzIikNCmN1cnJlbnRfY2VsbF9wcm9maWxlPC1zYWZlVE1FDQoNCiNzZWU6IGh0dHBzOi8vZ2l0aHViLmNvbS9OYW5vc3RyaW5nLUJpb3N0YXRzL0NlbGxQcm9maWxlTGlicmFyeS90cmVlL05ld1Byb2ZpbGVNYXRyaWNlcw0KDQpjdXJyZW50X2NlbGxfcHJvZmlsZSA8LSBkb3dubG9hZF9wcm9maWxlX21hdHJpeChzcGVjaWVzID0gIkh1bWFuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWdlX2dyb3VwID0gIkFkdWx0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF0cml4bmFtZSA9ICJLaWRuZXlfSENBIikNCg0KaGVhdG1hcChzd2VlcChjdXJyZW50X2NlbGxfcHJvZmlsZSwgMSwgYXBwbHkoY3VycmVudF9jZWxsX3Byb2ZpbGUsIDEsIG1heCksICIvIiksDQogICAgICAgIGxhYlJvdyA9IE5BLCBtYXJnaW5zID0gYygxMCwgNSksIGNleENvbCA9IDAuNykNCmBgYA0KDQojIDkuMyBSdW4gc3BhdGlhbCBkZWNvbnZvbHV0aW9uDQoNCmBgYHtyIHNwYXRpYWxfZGVjb25fcnVufQ0KIyB2ZWN0b3IgaWRlbnRpZnlpbmcgcHVyZSB0dW1vciBzZWdtZW50czoNCiN0YXJnZXRfRGF0YSRpc3R1bW9yID0gdGFyZ2V0X0RhdGEkQU5OMyA9PSAiQ09SRSIgJiB0YXJnZXRfRGF0YSRBTk4xID09ICJQYW5DSysiDQpyZXMgPSBydW5zcGF0aWFsZGVjb24ob2JqZWN0ID0gdGFyZ2V0X0RhdGEsDQogICAgICAgICAgICAgICAgICAgICAgbm9ybV9lbHQgPSAicV9ub3JtIiwNCiAgICAgICAgICAgICAgICAgICAgICByYXdfZWx0ID0gImV4cHJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAjaXNfcHVyZV90dW1vciA9IHRhcmdldF9EYXRhJGlzdHVtb3IsDQogICAgICAgICAgICAgICAgICAgICAgY2VsbF9jb3VudHMgPSB0YXJnZXRfRGF0YSRudWNsZWksDQogICAgICAgICAgICAgICAgICAgICAgWCA9IGN1cnJlbnRfY2VsbF9wcm9maWxlLA0KICAgICAgICAgICAgICAgICAgICAgICNjZWxsbWVyZ2VzID0gc2FmZVRNRS5tYXRjaGVzLCAgICAgICAgICAgICAgIyBzYWZlVE1FLm1hdGNoZXMgb2JqZWN0LCB1c2VkIGJ5IGRlZmF1bHQNCiAgICAgICAgICAgICAgICAgICAgICAjbl90dW1vcl9jbHVzdGVycyA9IDUsICAgICAgICAgICAgICAgICAgICAgICMgaG93IG1hbnkgZGlzdGluY3QgdHVtb3IgcHJvZmlsZXMgdG8gYXBwZW5kIHRvIHNhZmVUTUUNCiAgICAgICAgICAgICAgICAgICAgICBhbGlnbl9nZW5lcyA9IFRSVUUpDQoNCg0KYGBgDQoNCiMgOS4zLjEgU3BhdGlhbCBkZWNvbnZvbHV0aW9uIGhlYXRtYXBzIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNCiMjIEFidW5kYW5jZQ0KDQpgYGB7ciBzcGF0aWFsX2RlY29uX2hlYXRtYXAsIGZpZy53aWR0aD0yNSxmaWcuaGVpZ2h0PTE1fQ0KIyBOT1RFOiBjaGVjayBjbHVzdGVyaW5nLi4gd2h5IGRpZmZlcmVudD8NCg0KI3NldCBkaXNwbGF5IHRocmVzaG9sZHMNCnRocmVzaCA8LSBzaWduaWYocXVhbnRpbGUocmVzJGJldGEsIDAuOTcpLCAyKQ0KDQojIHBsb3Qgc3RvcmVkIHRvIGtlZXAgY2x1c3RlcmluZyBmb3IgbGF0ZXINCnAxPC1waGVhdG1hcChwbWluKHQocmVzJGJldGEpLHRocmVzaCksDQogICAgICAgICAjc2NhbGUgPSAicm93IiwgDQogICAgICAgICBjdXRyZWVfY29scyA9IDMsDQogICAgICAgICBjdXRyZWVfcm93cyA9IDIsDQogICAgICAgICBmb250c2l6ZV9yb3cgPSAxMiwNCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBUUlVFLCBzaG93X2NvbG5hbWVzID0gVFJVRSwNCiAgICAgICAgIGFuZ2xlX2NvbCA9ICI5MCIsDQogICAgICAgICBib3JkZXJfY29sb3IgPSBOQSwNCiAgICAgICAgICNjbHVzdGVyaW5nX21ldGhvZCA9ICJhdmVyYWdlIiwNCiAgICAgICAgICNjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICAgI2NsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgICBsZWdlbmRfYnJlYWtzID0gYyhyb3VuZChzZXEoMCwgdGhyZXNoLCBsZW5ndGgub3V0ID0gNSkpWy01XSwgdGhyZXNoKSwNCiAgICAgICAgIGxlZ2VuZF9sYWJlbHMgPSBjKHJvdW5kKHNlcSgwLCB0aHJlc2gsIGxlbmd0aC5vdXQgPSA1KSlbLTVdLCBwYXN0ZTAoIkFidW5kYW5jZSBzY29yZXMsXG50cnVuY2F0ZWQgYWJvdmUgYXQgIiwgdGhyZXNoKSksDQogICAgICAgICAjYnJlYWtzID0gc2VxKDAsIDUsIDEpLA0KICAgICAgICAgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKGMoIndoaXRlIiwiZGFya2JsdWUiKSkoMTAwKSwNCiAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gY29sb3JfbGlzdCwNCiAgICAgICAgIGFubm90YXRpb25fY29sID0gcERhdGEodGFyZ2V0X0RhdGEpWywgYW5uX25hbWVzXQ0KICAgICAgICAgKQ0KI3AxDQpgYGANCg0KIyMgUHJvcG9ydGlvbmFsDQoNCmBgYHtyIHNwYXRpYWxfZGVjb25fcHJvcGhlYXRtYXAsIGZpZy53aWR0aD0yNSxmaWcuaGVpZ2h0PTE1fQ0KIyBwcm9wb3J0aW9uczoNCnByb3BzIDwtIHJlcGxhY2UocmVzJHByb3Bfb2Zfbm9udHVtb3IsIGlzLm5hKHJlcyRwcm9wX29mX25vbnR1bW9yKSwgMCkNCg0KcDI8LXBoZWF0bWFwKHQocHJvcHMpLA0KICAgICAgICAgI3NjYWxlID0gInJvdyIsIA0KICAgICAgICAgY3V0cmVlX2NvbHMgPSAzLA0KICAgICAgICAgY3V0cmVlX3Jvd3MgPSAyLA0KICAgICAgICAgZm9udHNpemVfcm93ID0gMTIsDQogICAgICAgICBzaG93X3Jvd25hbWVzID0gVFJVRSwgc2hvd19jb2xuYW1lcyA9IFRSVUUsDQogICAgICAgICBhbmdsZV9jb2wgPSAiOTAiLA0KICAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEsDQogICAgICAgICAjY2x1c3RlcmluZ19tZXRob2QgPSAiYXZlcmFnZSIsDQogICAgICAgICAjY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgICNjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICAgbGVnZW5kX2JyZWFrcyA9IHJvdW5kKHNlcSgwLCBtYXgocHJvcHMpICogMC45OSwgbGVuZ3RoLm91dCA9IDUpLCAyKSwNCiAgICAgICAgIGxlZ2VuZF9sYWJlbHMgPSBjKHJvdW5kKHNlcSgwLCBtYXgocHJvcHMpLCBsZW5ndGgub3V0ID0gNSksIDIpWy01XSwgIlByb3BvcnRpb24gb2YgYWxsXG5maXR0ZWQgcG9wdWxhdGlvbnMiKSwNCiAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJ3aGl0ZSIsImRhcmtibHVlIikpKDEwMCksDQogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGNvbG9yX2xpc3QsDQogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IHBEYXRhKHRhcmdldF9EYXRhKVssIGFubl9uYW1lc10pDQoNCiNwMg0KDQpgYGANCg0KIyMgU2NhbGVkDQoNCmBgYHtyIHNwYXRpYWxfZGVjb25fc2NhbGVkaGVhdG1hcCwgZmlnLndpZHRoPTI1LGZpZy5oZWlnaHQ9MTV9DQojIHNjYWxlZCBhYnVuZGFuY2VzOg0KZXBzaWxvbiA8LSBtaW4ocmVzJGJldGFbcmVzJGJldGEgPiAwXSkNCm1hdCA8LSBzd2VlcChyZXMkYmV0YSwgMSwgcG1heChhcHBseShyZXMkYmV0YSwgMSwgbWF4KSwgZXBzaWxvbiksICIvIikNCg0KcGhlYXRtYXAodChtYXQpLA0KICAgICAgICAgI3NjYWxlID0gInJvdyIsDQogICAgICAgICBjdXRyZWVfY29scyA9IDMsDQogICAgICAgICBjdXRyZWVfcm93cyA9IDMsDQogICAgICAgICBmb250c2l6ZV9yb3cgPSAxMiwNCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBUUlVFLCBzaG93X2NvbG5hbWVzID0gVFJVRSwNCiAgICAgICAgIGFuZ2xlX2NvbCA9ICI5MCIsDQogICAgICAgICBib3JkZXJfY29sb3IgPSBOQSwNCiAgICAgICAgICNjbHVzdGVyaW5nX21ldGhvZCA9ICJhdmVyYWdlIiwNCiAgICAgICAgICNjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICAgI2NsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgICBsZWdlbmRfYnJlYWtzID0gYyhyb3VuZChzZXEoMCwgMSwgbGVuZ3RoLm91dCA9IDUpLCAyKVstNV0sIDEpLA0KICAgICAgICAgbGVnZW5kX2xhYmVscyA9IGMocm91bmQoc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSA1KSwgMilbLTVdLCAiU2NhbGVkIGFidW5kYW5jZVxuKHJhdGlvIHRvIG1heCkiKSwNCiAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJ3aGl0ZSIsImRhcmtibHVlIikpKDEwMCksDQogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGNvbG9yX2xpc3QsDQogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IHBEYXRhKHRhcmdldF9EYXRhKVssIGFubl9uYW1lc10pDQoNCmBgYA0KDQojIDkuNCBCYXJwbG90cyB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQojIyBhYnVuZGFuY2UNCg0KYGBge3IgU0RfYWJ1bmRhbmNlX2JhcnBsb3QsIGZpZy53aWR0aD0yNSxmaWcuaGVpZ2h0PTE1fQ0KIyBkZWZpbmUgdmFyaWFibGVzIHRvIHNob3cgaW4gaGVhdG1hcHM6DQpwRGF0YSh0YXJnZXRfRGF0YSkkcmVnaW9uIDwtIA0KICAgIGZhY3RvcihwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMywgdW5pcXVlKHRhcmdldF9EYXRhJEFOTjMpKSAgICMgTXVzdCBiZSBtYW51YWw/DQpwRGF0YSh0YXJnZXRfRGF0YSkkY2xhc3MgPC0gDQogICAgZmFjdG9yKHBEYXRhKHRhcmdldF9EYXRhKSRBTk4xLCB1bmlxdWUodGFyZ2V0X0RhdGEkQU5OMSkpICAgIyBNdXN0IGJlIG1hbnVhbD8NCg0KdmFyaWFibGVzX3RvX3Bsb3QgPC0gYygic2xpZGVfbmFtZSIsICJBTk4xIiwgIkFOTjMiKSAgICAgICAgICAgICAgICAgICAgICMgTXVzdCBiZSBtYW51YWw/DQoNCiNkZWZpbmUgY29sb3JzDQoNCiMgb25seSBmb3Igc2FmZVRNRSBjb2xvcnMNCiNjb2wgPC0gY2VsbGNvbHMNCg0KI2N1c3RvbSBjZWxtYXRyaXgNCiNnZXQgbGFyZ2UgbnVtYmVyIG9mIGNvbG9ycw0KcXVhbF9jb2xfcGFscyA9IGJyZXdlci5wYWwuaW5mb1ticmV3ZXIucGFsLmluZm8kY2F0ZWdvcnkgPT0gJ3F1YWwnLF0NCmNvbF92ZWN0b3IgPSB1bmlxdWUodW5saXN0KG1hcHBseShicmV3ZXIucGFsLCBxdWFsX2NvbF9wYWxzJG1heGNvbG9ycywgcm93bmFtZXMocXVhbF9jb2xfcGFscykpKSkNCmNlbGx0eXBlczwtc2FtcGxlKGNvbF92ZWN0b3IsbGVuZ3RoKGNvbG5hbWVzKHJlcyRiZXRhKSkpDQpuYW1lcyhjZWxsdHlwZXMpPC1jb2xuYW1lcyhyZXMkYmV0YSkNCmNvbDwtY2VsbHR5cGVzDQoNCg0KI3RlbXBmaXggZm9yIGFiYnJldmlhdGVkIGFuZCBub3cgbWlzbWF0Y2hpbmcgYW5ub3RhdGlvbnMNCiNjYW4ganVzdCB1c2UgYW5uIGRhdGFmcmFtZT8NCiN0bXBhbm48LWNiaW5kKEFOTjEsQU5OMixTTikNCnRtcGFubiA8LSBwRGF0YSh0YXJnZXRfRGF0YSlbYW5uX25hbWVzXQ0KDQoNCg0KbGF5b3V0KG1hdHJpeChjKDEsIDIsIDMsIDMpLCBucm93ID0gMiksDQogICAgICAgd2lkdGhzID0gYygxMCwgMywgMTAsIDMpLA0KICAgICAgIGhlaWdodHMgPSBjKDEsIDgsIDEwKSwNCiAgICAgICkNCg0KcGFyKG1hciA9IGMoMCwgOC4yLCAwLCAwLjIpKQ0KcGxvdChwMSR0cmVlX2NvbCwgbGFiZWxzID0gRiwgbWFpbiA9ICIiLCB5bGFiID0gIiIsIHlheHQgPSAibiIpDQpwYXIobWFyID0gYygxNSwgOCwgMCwgMCkpDQoNCiMgZGF0YSB0byBwbG90Og0KbWF0IDwtIHQocmVzJGJldGEpWywgcDEkdHJlZV9jb2wkb3JkZXJdDQojIGluZmVyIHNjYWxlIG9mIG5lZ2F0aXZlIHktYXhpcyBmb3IgYW5ub3RhdGlvbiBjb2xvcmJhcnMNCnltaW4gPC0gLW1heChjb2xTdW1zKG1hdCkpICogMC4xNQ0KaWYgKCFpcy5maW5pdGUoeW1pbikpIHsNCiAgeW1pbiA8LSAwDQp9DQoNCiMgZHJhdyBiYXJwbG90Og0KYnAgPC0gYmFycGxvdChtYXQsDQogICAgICAgICAgICAgIGNleC5sYWIgPSAxLjUsDQogICAgICAgICAgICAgIGNvbCA9IGNvbCwgYm9yZGVyID0gTkEsDQogICAgICAgICAgICAgIGNleC5uYW1lcyA9IDEuMSwNCiAgICAgICAgICAgICAgbGFzID0gMiwgbWFpbiA9ICIiLCB5bGFiID0gIkFidW5kYW5jZSBzY29yZXMiLA0KICAgICAgICAgICAgICB5bGltID0gYyh5bWluLCBtYXgoY29sU3VtcyhtYXQpKSkNCikNCg0KDQojIGFkZCBjb2xvciBiYXJzIGZvciBhbm5vdGF0aW9ucw0KZm9yIChuYW1lIGluIHJldih2YXJpYWJsZXNfdG9fcGxvdCkpIHsNCiAgeXJhbmdlIDwtIHNlcSh5bWluIC8gMywgeW1pbiwgbGVuZ3RoLm91dCA9IGxlbmd0aCh2YXJpYWJsZXNfdG9fcGxvdCkgKyAxKVttYXRjaChuYW1lLCB2YXJpYWJsZXNfdG9fcGxvdCkgKyBjKDAsIDEpXQ0KICB4d2lkdGggPC0gKGJwWzJdIC0gYnBbMV0pIC8gMg0KICBmb3IgKGkgaW4gMTpuY29sKG1hdCkpIHsNCiAgICByZWN0KGJwW2ldIC0geHdpZHRoLCB5cmFuZ2VbMl0sIGJwW2ldICsgeHdpZHRoLCB5cmFuZ2VbMV0sDQogICAgICAgICAjIGJvcmRlciA9IE5BLCBjb2wgPSBhbm5fY29sb3JzW1tuYW1lXV1bc2VnbWVudEFubm90YXRpb25zW21hdGNoKGNvbG5hbWVzKG1hdClbaV0sIHNlZ21lbnRBbm5vdGF0aW9ucyRzZWdtZW50SUQpLCBuYW1lXV0NCiAgICAgICAgICNib3JkZXIgPSBOQSwgY29sID0gYW5uX2NvbG9yc1tbbmFtZV1dW2FubltwMSR0cmVlX2NvbCRvcmRlcltpXSwgbmFtZV1dDQogICAgICAgICBib3JkZXIgPSBOQSwgY29sID0gY29sb3JfbGlzdFtbbmFtZV1dW3RtcGFubltjb2xuYW1lcyhtYXQpW2ldLCBuYW1lXV0NCiAgICApDQogIH0NCn0NCmF4aXMoMiwNCiAgICAgYXQgPSBzZXEoeW1pbiAvIDMsIHltaW4sIGxlbmd0aC5vdXQgPSBsZW5ndGgodmFyaWFibGVzX3RvX3Bsb3QpICsgMilbLWMoMSwgbGVuZ3RoKHZhcmlhYmxlc190b19wbG90KSArIDIpXSwNCiAgICAgbGFzID0gMiwgbGFiZWxzID0gdmFyaWFibGVzX3RvX3Bsb3QsIGx0eSA9IDAsIGNleC5heGlzID0gMS4yDQopDQoNCiNkcmF3IGEgbGVnZW5kOg0KcGFyKG1hciA9IGMoMC4xLCAwLjEsIDAuMSwgMC4xKSkNCmZyYW1lKCkNCmxlZ2VuZGNvbHMgPC0gbGVnZW5kbmFtZXMgPC0gYygpDQojZm9yIChuYW1lIGluIHJldihuYW1lcyhhbm5fY29sb3JzKSkpIHsNCmZvciAobmFtZSBpbiBjKCJzbGlkZV9uYW1lIiwgIkFOTjEiLCAiQU5OMyIpKSB7DQogIGxlZ2VuZGNvbHMgPC0gYyhsZWdlbmRjb2xzLCBOQSwgY29sb3JfbGlzdFtbbmFtZV1dLCBOQSkNCiAgbGVnZW5kbmFtZXMgPC0gYyhsZWdlbmRuYW1lcywgbmFtZSwgbmFtZXMoY29sb3JfbGlzdFtbbmFtZV1dKSwgTkEpDQp9DQpsZWdlbmQoImNlbnRlciIsDQogICAgICAgcGNoID0gMTUsDQogICAgICAgY2V4ID0gMS41LA0KICAgICAgIGNvbCA9IGMobGVnZW5kY29scywgcmVwKE5BLCAxKSwgcmV2KGNvbCkpLA0KICAgICAgIGxlZ2VuZCA9IGMobGVnZW5kbmFtZXMsICJDZWxsIHR5cGUiLCByZXYobmFtZXMoY29sKSkpLA0KKQ0KYGBgDQoNCiMjIHByb3BvcnRpb25hbA0KDQpgYGB7ciBTRF9wcm9wX2JhcnBsb3QsIGZpZy53aWR0aD0yNSxmaWcuaGVpZ2h0PTE1fQ0KIyBkZWZpbmUgdmFyaWFibGVzIHRvIHNob3cgaW4gaGVhdG1hcHM6DQp2YXJpYWJsZXNfdG9fcGxvdCA8LSBjKCJzbGlkZV9uYW1lIiwgIkFOTjEiLCAiQU5OMyIpDQoNCmxheW91dChtYXRyaXgoYygxLCAyLCAzLCAzKSwgbnJvdyA9IDIpLA0KICAgICAgIHdpZHRocyA9IGMoMTAsIDMsIDEwLCAzKSwNCiAgICAgICBoZWlnaHRzID0gYygxLCA4LCAxMCksDQogICAgICApDQoNCnBhcihtYXIgPSBjKDAsIDguMiwgMCwgMC4yKSkNCnBsb3QocDIkdHJlZV9jb2wsIGxhYmVscyA9IEYsIG1haW4gPSAiIiwgeWxhYiA9ICIiLCB5YXh0ID0gIm4iKQ0KcGFyKG1hciA9IGMoMTUsIDgsIDAsIDApKQ0KDQojIGRhdGEgdG8gcGxvdDoNCm1hdCA8LSB0KHJlcyRwcm9wX29mX25vbnR1bW9yKVssIHAyJHRyZWVfY29sJG9yZGVyXQ0KICBtYXQgPC0gcmVwbGFjZShtYXQsIGlzLm5hKG1hdCksIDApDQogICMgaW5mZXIgc2NhbGUgb2YgbmVnYXRpdmUgeS1heGlzIGZvciBhbm5vdGF0aW9uIGNvbG9yYmFycw0KICB5bWluIDwtIC0wLjE1DQoNCiMgZHJhdyBiYXJwbG90Og0KYnAgPC0gYmFycGxvdChtYXQsDQogICAgICAgICAgICAgIGNleC5sYWIgPSAxLjUsDQogICAgICAgICAgICAgIGNvbCA9IGNvbCwgYm9yZGVyID0gTkEsDQogICAgICAgICAgICAgIGNleC5uYW1lcyA9IDEuMSwNCiAgICAgICAgICAgICAgbGFzID0gMiwgbWFpbiA9ICIiLCB5bGFiID0gIlByb3BvcnRpb24gb2YgZml0dGVkIGNlbGxzIiwNCiAgICAgICAgICAgICAgeWxpbSA9IGMoeW1pbiwgbWF4KGNvbFN1bXMobWF0KSkpDQopDQoNCiMgYWRkIGNvbG9yIGJhcnMgZm9yIGFubm90YXRpb25zDQpmb3IgKG5hbWUgaW4gcmV2KHZhcmlhYmxlc190b19wbG90KSkgew0KICB5cmFuZ2UgPC0gc2VxKHltaW4gLyAzLCB5bWluLCBsZW5ndGgub3V0ID0gbGVuZ3RoKHZhcmlhYmxlc190b19wbG90KSArIDEpW21hdGNoKG5hbWUsIHZhcmlhYmxlc190b19wbG90KSArIGMoMCwgMSldDQogIHh3aWR0aCA8LSAoYnBbMl0gLSBicFsxXSkgLyAyDQogIGZvciAoaSBpbiAxOm5jb2wobWF0KSkgew0KICAgIHJlY3QoYnBbaV0gLSB4d2lkdGgsIHlyYW5nZVsyXSwgYnBbaV0gKyB4d2lkdGgsIHlyYW5nZVsxXSwNCiAgICAgICAgICMgYm9yZGVyID0gTkEsIGNvbCA9IGFubl9jb2xvcnNbW25hbWVdXVtzZWdtZW50QW5ub3RhdGlvbnNbbWF0Y2goY29sbmFtZXMobWF0KVtpXSwgc2VnbWVudEFubm90YXRpb25zJHNlZ21lbnRJRCksIG5hbWVdXQ0KICAgICAgICAgI2JvcmRlciA9IE5BLCBjb2wgPSBhbm5fY29sb3JzW1tuYW1lXV1bYW5uW3AyJHRyZWVfY29sJG9yZGVyW2ldLCBuYW1lXV0NCiAgICAgICAgIGJvcmRlciA9IE5BLCBjb2wgPSBjb2xvcl9saXN0W1tuYW1lXV1bdG1wYW5uW2NvbG5hbWVzKG1hdClbaV0sIG5hbWVdXQ0KICAgICkNCiAgfQ0KfQ0KYXhpcygyLA0KICAgICBhdCA9IHNlcSh5bWluIC8gMywgeW1pbiwgbGVuZ3RoLm91dCA9IGxlbmd0aCh2YXJpYWJsZXNfdG9fcGxvdCkgKyAyKVstYygxLCBsZW5ndGgodmFyaWFibGVzX3RvX3Bsb3QpICsgMildLA0KICAgICBsYXMgPSAyLCBsYWJlbHMgPSB2YXJpYWJsZXNfdG9fcGxvdCwgbHR5ID0gMCwgY2V4LmF4aXMgPSAxLjINCikNCg0KDQojZHJhdyBhIGxlZ2VuZDoNCnBhcihtYXIgPSBjKDAuMSwgMC4xLCAwLjEsIDAuMSkpDQpmcmFtZSgpDQpsZWdlbmRjb2xzIDwtIGxlZ2VuZG5hbWVzIDwtIGMoKQ0KI2ZvciAobmFtZSBpbiByZXYobmFtZXMoYW5uX2NvbG9ycykpKSB7DQpmb3IgKG5hbWUgaW4gYygic2xpZGVfbmFtZSIsICJBTk4xIiwgIkFOTjMiKSkgew0KICBsZWdlbmRjb2xzIDwtIGMobGVnZW5kY29scywgTkEsIGNvbG9yX2xpc3RbW25hbWVdXSwgTkEpDQogIGxlZ2VuZG5hbWVzIDwtIGMobGVnZW5kbmFtZXMsIG5hbWUsIG5hbWVzKGNvbG9yX2xpc3RbW25hbWVdXSksIE5BKQ0KfQ0KbGVnZW5kKCJjZW50ZXIiLA0KICAgICAgIHBjaCA9IDE1LA0KICAgICAgIGNleCA9IDEuNCwNCiAgICAgICBjb2wgPSBjKGxlZ2VuZGNvbHMsIHJlcChOQSwgMSksIHJldihjb2wpKSwNCiAgICAgICBsZWdlbmQgPSBjKGxlZ2VuZG5hbWVzLCAiQ2VsbCB0eXBlIiwgcmV2KG5hbWVzKGNvbCkpKSwNCikNCmBgYA0KDQojIyBCb3hwbG90cw0KDQpCb3hwbG90IHBlciBjZWxsIGZyb20gdGhlIHNwYXRpYWwgZGVjb252b2x1dGlvbi4gQ2hvb3NlIHlvdXIgYW5ub3RhdGlvbiBpbiB3aGljaCB5b3Ugd2FudCB0byBjb21wYXJlIChBTk4pLiANClRoZSBhbm5vdGF0aW9uIHdpbGwgYmUgZGl2aWRlZCBpbiBjb25kaXRpb25zIGFuZCBlYWNoIGNlbGwgd2l0aCBoYXZlIGEgYm94cGxvdCB3aGljaCBpbmNsdWRlIHRoZSBhYnVuZGFuY2Ugc2NvcmUgb2YgZWFjaCBjb25kaXRpb24uDQpBIHN0YXRpc3RpY2FsIHRlc3QgaGFzIGJlZW4gYWRkZWQgaW5zaWRlIHRoZSBib3hwbG90IHRvIGNoZWNrIGZvciBzaWduaWZpY2FuY2UuIEZlZWwgZnJlZSB0byBjaGFuY2UgdGhlIHRlc3QgdG8gZml0IHRoZSBkYXRhLg0KDQpzaWduaWZpY2FuY2UgY29kZSAgICAgICAgIHAtdmFsdWUNCiAgICoqKiAgICAgICAgICAgICAgICAgWzAsIDAuMDAxXQ0KICAgICoqICAgICAgICAgICAgICAoMC4wMDEsIDAuMDFdDQogICAgICogICAgICAgICAgICAgICAoMC4wMSwgMC4wNV0NCiAgICAgLiAgICAgICAgICAgICAgICAoMC4wNSwgMC4xXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICgwLjEsIDFdIA0KDQpgYGB7ciBmaWcuaGVpZ2h0PTQwLCBmaWcud2lkdGg9NDAsIHdhcm5pbmc9RkFMU0V9DQpBTk4gPC0gIkFOTjEiICMgQU5OIGNvbHVtbiB3aXRoIGNvbmRpdGlvbnMNCmlmIChsZW5ndGgodW5pcXVlKHRhcmdldF9EYXRhW1tBTk5dXSkpID4gMikgew0KICB0ZXN0IDwtICJrcnVza2FsLnRlc3QiDQp9IGVsc2Ugew0KICB0ZXN0IDwtICJ3aWxjb3gudGVzdCINCn0NCg0KcmVzX2ZpbHRlciA8LSByZXNbIXJlc0BwaGVub0RhdGFAZGF0YVtbImJldGEiXV09PTBdDQpyZXNfZmlsdGVyIDwtIGFzLmRhdGEuZnJhbWUocmVzX2ZpbHRlciRiZXRhKQ0KcmVzX2ZpbHRlcltyZXNfZmlsdGVyID09IDBdIDwtIE5BDQpib3hwbG90X3ByZXAgPC0gcmVzX2ZpbHRlclsgLCBjb2xTdW1zKGlzLm5hKHJlc19maWx0ZXIpKSA8IG5yb3cocmVzX2ZpbHRlcildIA0KDQpib3hwbG90X3ByZXAkY29uZGl0aW9uIDwtIHJvd25hbWVzKGJveHBsb3RfcHJlcCkNCg0KZm9yICh2YWx1ZSBpbiB1bmlxdWUodGFyZ2V0X0RhdGFbW0FOTl1dKSkgew0KICBjb24gPC0gcm93bmFtZXMocERhdGEodGFyZ2V0X0RhdGEpW3BEYXRhKHRhcmdldF9EYXRhKVtbQU5OXV0gPT0gdmFsdWUsXSkNCiAgYm94cGxvdF9wcmVwJGNvbmRpdGlvbltib3hwbG90X3ByZXAkY29uZGl0aW9uICVpbiUgY29uXSA8LSB2YWx1ZQ0KfQ0KDQpib3hwbG90IDwtIG1lbHQoYm94cGxvdF9wcmVwKQ0KDQojIGZ1bmN0aW9uIGZvciBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIA0KZ2l2ZS5uIDwtIGZ1bmN0aW9uKHgpew0KICByZXR1cm4oZGF0YS5mcmFtZSh5ID0gLTEsIGxhYmVsID0gcGFzdGUwKCJuID0gIixsZW5ndGgoeCkpKSkNCiAjcmV0dXJuKGMoeSA9IG1lZGlhbih4KSoxLjA1LCBsYWJlbCA9IGxlbmd0aCh4KSkNCiMgZXhwZXJpbWVudCB3aXRoIHRoZSBtdWx0aXBsaWVyIHRvIGZpbmQgdGhlIHBlcmZlY3QgcG9zaXRpb24NCn0NCg0KZ2dwbG90KGJveHBsb3QsIGFlcyhmYWN0b3IoY29uZGl0aW9uKSwgdmFsdWUsIGZpbGwgPSBjb25kaXRpb24pKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICBnZW9tX2ppdHRlcihwb3NpdGlvbj1wb3NpdGlvbl9qaXR0ZXIodz0wLjEsaD0wLjEpKSArDQogIGZhY2V0X3dyYXAofnZhcmlhYmxlLCBzY2FsZT0iZnJlZSIsIG5jb2wgPSA1KSArDQogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKw0KICByb3RhdGVfeF90ZXh0KGFuZ2xlID0gNDUpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJTcGF0aWFsIERlY29udm9sdXRpb24iLA0KICAgIHggPSAiQ29uZGl0aW9ucyIsDQogICAgeSA9ICJBYnVuY2FuY2Ugc2NvcmUiKSArIA0KICBzdGF0X2NvbXBhcmVfbWVhbnMobWV0aG9kID0gdGVzdCwgbGFiZWwueSA9IC0zLCBzaXplPTUpICsgDQogIHN0YXRfY29tcGFyZV9tZWFucyhsYWJlbCA9ICJwLnNpZ25pZiIsIG1ldGhvZCA9ICJ0LnRlc3QiLA0KICAgICAgICAgICAgICAgICAgICAgcmVmLmdyb3VwID0gIi5hbGwuIiwgaGlkZS5ucyA9IFRSVUUsIHNpemU9NSkgKw0KICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBnaXZlLm4sIGdlb20gPSAidGV4dCIsIHNpemUgPSA1KQ0KDQoNCiNzdGF0X2NvbXBhcmVfbWVhbnMoc2l6ZT01KQ0KYGBgDQoNCg0KIyAxMSBDTlYNCkNvcHkgTnVtYmVyIFZhcmlhdGlvbiBhbmFseXNpcyB3aXRoIHRoZSBSIHNvZnR3YXJlIHBhY2thZ2UgaW5mZXJDTlYgKGh0dHBzOi8vZ2l0aHViLmNvbS9icm9hZGluc3RpdHV0ZS9pbmZlckNOVi93aWtpKS4NCg0KSW5mZXJDTlYgaXMgdXNlZCB0byBleHBsb3JlIHR1bW9yIHNpbmdsZSBjZWxsIFJOQS1TZXEgZGF0YSB0byBpZGVudGlmeSBldmlkZW5jZSBmb3Igc29tYXRpYyBsYXJnZS1zY2FsZSBjaHJvbW9zb21hbCBjb3B5IG51bWJlciBhbHRlcmF0aW9ucywgc3VjaCBhcyBnYWlucyBvciBkZWxldGlvbnMgb2YgZW50aXJlIGNocm9tb3NvbWVzIG9yIGxhcmdlIHNlZ21lbnRzIG9mIGNocm9tb3NvbWVzLiBUaGlzIGlzIGRvbmUgYnkgZXhwbG9yaW5nIGV4cHJlc3Npb24gaW50ZW5zaXR5IG9mIGdlbmVzIGFjcm9zcyBwb3NpdGlvbnMgb2YgdHVtb3IgZ2Vub21lIGluIGNvbXBhcmlzb24gdG8gYSBzZXQgb2YgcmVmZXJlbmNlICdub3JtYWwnIGNlbGxzLiBBIGhlYXRtYXAgaXMgZ2VuZXJhdGVkIGlsbHVzdHJhdGluZyB0aGUgcmVsYXRpdmUgZXhwcmVzc2lvbiBpbnRlbnNpdGllcyBhY3Jvc3MgZWFjaCBjaHJvbW9zb21lLCBhbmQgaXQgb2Z0ZW4gYmVjb21lcyByZWFkaWx5IGFwcGFyZW50IGFzIHRvIHdoaWNoIHJlZ2lvbnMgb2YgdGhlIHR1bW9yIGdlbm9tZSBhcmUgb3Zlci1hYnVuZGFudCBvciBsZXNzLWFidW5kYW50IGFzIGNvbXBhcmVkIHRvIHRoYXQgb2Ygbm9ybWFsIGNlbGxzLg0KDQpQZXIgcGF0aWVudCBhIGNudiBhbmFseXNpcyB3aXRoIGEgcHJvdmlkZWQgcmVmZXJlbmNlIGdyb3VwLiBUaGUgZ2VuZSBvcmRlciBpcyBtYWRlIGZyb20gdGhlIHByb2plY3RzIC5wa2MgZmlsZS4NCg0KU2VsZWN0IHRoZSBhbm5vdGF0aW9uIHdoZXJlIHRoZSBDTlYgYW5hbHlzaXMgc2hvdWxkIGxvb2sgYXQgc3BlY2lmaWNhbGx5IGFzIGdyb3VwIGFuZCBzcGVjaWZ5IGEgc3ViZ3JvdXAgYXMgZ3JvdXBfZmlsdGVyIGlmIHRoZSBncm91cCBvZiBpbnRlcmVzdCBpcyBhIHN1Ymdyb3VwIGluc2lkZSB0aGUgYW5ub3RhdGlvbi4gRm9yIGV4YW1wbGUgaWYgdGhlIENOViBhbmFseXNpcyBuZWVkcyB0byBiZSBvbmx5IG9uIHR1bW9yIHJlZ2lvbnMgdGhlIGdyb3VwIHdvdWxkIGJlIEFOTlgoQU5OIGNvbHVtbiB3aXRoIHRoZSBpbmZvIGFib3V0IHRoZSB0dW1vciByZWdpb25zKSBhbmQgZ3JvdXBfZmlsdGVyIFBhbkNLKy4gDQpUaGUgYW5ub3RhdGlvbiB0aGF0IHJlZmVyZW5jZXMgdGhlIHBhdGllbnRzIGFzIHBhdGllbnRzLiBJZiB0aGVyZSBhcmUgbm8gcGF0aWVudHMgbGVhdmUgdGhlIHBhcmFtZXRlciBlbXB0eSBhcyAiIi4NClRoZSBhbm5vdGF0aW9uIHRoYXQgc2hvdWxkIGJlIGluY2x1ZGVkIGluIHRoZSByZXN1bHRzIGluY2x1ZGluZyBhIHJlZmVyZW5jZSBzZXQgYXMgY252X3RhcmdldC4NClRoaXMgd2lsbCBjcmVhdGUgcGF0aWVudCBzcGVjaWZpYyBmaWxlcyB3aXRoIGFsbCB0aGUgaW5mb3JtYXRpb24gaW5mZXJDTlYgbmVlZHMuDQoNCmBgYHtyIGNyZWF0ZSBjbnYgZmlsZXMsIGluY2x1ZGU9RkFMU0V9DQojIyMjIyMjIyNQQVJBTUVURVJTIyMjIyMjIyMNCmdyb3VwIDwtICIiDQpncm91cF9maWx0ZXIgPC0gIiINCg0KcGF0aWVudHMgPC0gIiINCg0KY252X3RhcmdldCA8LSAiQU5OMSINCnByaW50KHBhc3RlKCJTaG93IENOViBvbiIsIGNudl90YXJnZXQsICJ3aXRoIHRoZSBzcGVjaWZpY2l0eSBvbiB0aGUgZ3JvdXBzIChpZiBhbnkpOiIsIGdyb3VwLCAiOyIsIGdyb3VwX2ZpbHRlcikpDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KaWYgKCFwYXRpZW50cyA9PSAiIikgew0KICBwYXRpZW50X2xpc3QgPC0gdW5pcXVlKHRhcmdldF9EYXRhW1twYXRpZW50c11dKQ0KfSBlbHNlew0KICBwYXRpZW50X2xpc3QgPC0gImR1bW15Ig0KfQ0KDQpmaWxlIDwtIHJlYWRMaW5lcyhQS0NGaWxlcykNCmZpbGUgPC0gZ3N1YignICcsICcnLA0KICAgICAgICBnc3ViKCciJywgJycsIGZpbGUpKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBGb3JtYXQgYW5kIHJlbW92ZSBzcGFjZQ0KDQpkaXNwbGF5IDwtIGZpbGVbZ3JlcGwoIkRpc3BsYXlOYW1lIiwgZmlsZSwgZml4ZWQgPSBUUlVFKV0gICAgICAgICAjIEdyYWIgZGlzcGxheSBuYW1lcw0KZGlzcGxheSA8LSBnc3ViKCdEaXNwbGF5TmFtZScsICIiLA0KICAgICAgICAgICBnc3ViKCciJywgIiIsDQogICAgICAgICAgIGdzdWIoJzonLCAiIiwNCiAgICAgICAgICAgZ3N1YignLCcsICIiLA0KICAgICAgICAgICBnc3ViKCcgJywgIiIsDQogICAgICAgICAgIGdzdWIoJ18wMScsICIiLCBkaXNwbGF5KSkpKSkpICAgICAgICAgICAgICAgICAgICAgICAgICAjIEdyYWIgb25seSB0aGUgbmFtZXMNCmRpc3BsYXkgPC0gdW5pcXVlKGRpc3BsYXkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUmVtb3ZlIGR1cGxpY2F0ZXMNCg0KY2hyIDwtIGZpbGVbZ3JlcCgiR2Vub21lQ29vcmRpbmF0ZXMiLCBmaWxlKSsxXSAgICAgICAgICAgICAgICAgICAgIyBHZXQgdGhlIGNociBwb3NpdGlvbnMgdW5kZXIgdGhlIEdlbm9tZUNvb3JkaW5hdGVzIGxpbmUNCmNociA8LWdzdWIoJywnLCAiIiwgY2hyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUmVtb3ZlIHVud2FudGVkIHN5bWJvbHMNCg0KcG9zaXRpb25zIDwtIGRhdGEuZnJhbWUoDQogIG5hbWUgPSBkaXNwbGF5LA0KICBjaHIgPSBjaHIpDQpwb3NpdGlvbnMgPC0gcG9zaXRpb25zWyFncmVwbCgiVGFyZ2V0U2VxdWVuY2UiLCBwb3NpdGlvbnMkY2hyKSxdICAjIFJlbW92ZSBlbnRyaWVzIHdpdGhvdXQgY29vcmRpbmF0ZXMNCg0KcG9zaXRpb25zJGNociA8LSBnc3ViKCc6JywgJy0nLCBwb3NpdGlvbnMkY2hyKSAgICAgICAgICAgICAgICAgICAgIyBGb3JtYXQgZm9yIHNwbGl0dGluZw0KcG9zaXRpb25zJGNociA8LSBzdHJfc3BsaXRfZml4ZWQocG9zaXRpb25zJGNociwgIi0iLCAzKSAgICAgICAgICAgIyBTcGxpdCBpbnRvIGNociwgYmVnaW4sIGFuZCBlbmQgcG9zaXRpb24NCnBvc2l0aW9ucyA8LSBhcy5tYXRyaXgocG9zaXRpb25zKQ0KcG9zaXRpb25zIDwtIGFzLmRhdGEuZnJhbWUocG9zaXRpb25zKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTd2FwIHR5cGVzIHRvIHJlY29nbml6ZSBzcGxpdCBjb2x1bW5zIGFzIHNpbmd1bGFyIGNvbHVtbnMNCg0KcG9zaXRpb25zJG9yZGVyIDwtIGFzLm51bWVyaWMoZ3N1YignY2hyJywgJycsIHBvc2l0aW9ucyRjaHIuMSkpDQpwb3NpdGlvbnMgPC0gcG9zaXRpb25zW29yZGVyKHBvc2l0aW9ucyRvcmRlciwgZGVjcmVhc2luZyA9IEZBTFNFKSwgXSAjIE9yZGVyIHRoZSBjaHJvbW9zb21lcy4NCnBvc2l0aW9ucyRvcmRlciA8LSBOVUxMDQoNCmNvbG5hbWVzKHBvc2l0aW9ucykgPC0gTlVMTA0Kcm93bmFtZXMocG9zaXRpb25zKSA8LSBOVUxMICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBpbmZlckNOViByZXF1aXJlcyBubyBjb2x1bW4gYW5kIHJvdyBuYW1lcw0KDQp3cml0ZS50YWJsZShwb3NpdGlvbnMsICJnZW5lX29yZGVyX0hzX1dUQV92MV9wa2MudHh0IiwNCiAgICAgICAgICAgIHNlcCA9ICJcdCIsDQogICAgICAgICAgICBxdW90ZSA9IEZBTFNFLA0KICAgICAgICAgICAgY29sLm5hbWVzID0gRkFMU0UsDQogICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFdyaXRlIGF3YXkNCg0KDQpmb3IgKHBhdGllbnQgaW4gcGF0aWVudF9saXN0KSB7DQogIGlmIChncm91cF9maWx0ZXIgIT0gIiIpIHsNCiAgICBncm91cF9pbnRlcmVzdCA8LSB0YXJnZXRfRGF0YUBwcm90b2NvbERhdGFAZGF0YVtbIlNhbXBsZUlEIl1dW3RhcmdldF9EYXRhW1tncm91cF1dID09IGdyb3VwX2ZpbHRlciAmIHRhcmdldF9EYXRhW1twYXRpZW50c11dID09IHBhdGllbnRdDQogIH0gZWxzZSBpZiAobGVuZ3RoKHBhdGllbnRfbGlzdCkgPiAxKSB7DQogICAgZ3JvdXBfaW50ZXJlc3QgPC0gdGFyZ2V0X0RhdGFAcHJvdG9jb2xEYXRhQGRhdGFbWyJTYW1wbGVJRCJdXVt0YXJnZXRfRGF0YVtbcGF0aWVudHNdXSA9PSBwYXRpZW50XQ0KICB9IGVsc2Ugew0KICAgIGdyb3VwX2ludGVyZXN0IDwtIHRhcmdldF9EYXRhQHByb3RvY29sRGF0YUBkYXRhW1siU2FtcGxlSUQiXV0NCiAgfQ0KICANCiAgY291bnRzIDwtIHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dDQogIGNvbG5hbWVzKGNvdW50cykgPC0gZ3N1YignLmRjYycsICcnLGNvbG5hbWVzKGNvdW50cykpDQogIGNvdW50cyA8LSBjb3VudHNbLGNvbG5hbWVzKGNvdW50cykgJWluJSBjKGdyb3VwX2ludGVyZXN0KV0NCiAgd3JpdGUudGFibGUoY291bnRzLCBwYXN0ZTAocGF0aWVudCwgIi5Db3VudHMudHN2IiksIHNlcCA9ICJcdCIpICAgICAgICAgICAgICAgICAgICAjIE1ha2UgcmF3IGNvdW50IGZpbGUNCg0KICBhbm5vdGF0aW9uIDwtIHRhcmdldF9EYXRhQHBoZW5vRGF0YUBkYXRhDQogIGFubm90YXRpb24kU2FtcGxlSUQgPC0gZ3N1YignLmRjYycsJycsIHJvd25hbWVzKGFubm90YXRpb24pKQ0KICBhbm5vdGF0aW9uIDwtIGFubm90YXRpb25bYW5ub3RhdGlvbiRTYW1wbGVJRCAlaW4lIGMoZ3JvdXBfaW50ZXJlc3QpLF0NCiAgI2Fubm90YXRpb24gPC0gYW5ub3RhdGlvbltjKCJTYW1wbGVJRCIsICJzZWdtZW50IildICAgICAgICAgICAgICAgICAgIyBTZWxlY3QgU2FtcGxlSUQgYW5kIHRoZSBuZWVkZWQgYW5ub3RhdGlvbiAoQ05WIHJlcXVpcmVzIFNhbXBsZUlEIGFuZCBvbmx5IDEgYW5ub3RhdGlvbikNCiAgYW5ub3RhdGlvbiA8LSBhbm5vdGF0aW9uW2MoIlNhbXBsZUlEIiwgY252X3RhcmdldCldDQogIHJvd25hbWVzKGFubm90YXRpb24pIDwtIE5VTEwNCiAgY29sbmFtZXMoYW5ub3RhdGlvbikgPC0gTlVMTCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluZmVyQ05WIHJlcXVpcmVzIG5vIGNvbHVtbiBhbmQgcm93IG5hbWVzDQogIHdyaXRlLnRhYmxlKGFubm90YXRpb24sIHBhc3RlMChwYXRpZW50LCAiLkFubm90YXRpb25zLnRzdiIpLA0KICAgICAgICAgICAgICBzZXAgPSAiXHQiLA0KICAgICAgICAgICAgICBxdW90ZSA9IEZBTFNFLA0KICAgICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwNCiAgICAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1ha2UgYW5ub3RhdGlvbiBmaWxlDQp9DQpgYGANCg0KU2VsZWN0IHRoZSByZWZlcmVuY2Ugc2V0IGluIHJlZmVyZW5jZS4gVGhpcyBjYW4gYmUgbW9yZSB0aGFuIG9uZS4gRXZlcnkgcGF0aWVudCB3aXRob3V0IGl0cyBvd24gcmVmZXJlbmNlIHdpbGwgbm90IGJlIGFuYWx5c2VkLg0KDQpgYGB7ciBydW4gQ05WLCBlY2hvPVQsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KIyMjIyMjIyMjUEFSQU1FVEVSUyMjIyMjIyMjDQpyZWZlcmVuY2UgPC0gYygibm9ybWFsIikNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQpmYWlsZWQgPC0gYygpDQpjbnZfbGlzdCA8LSBsaXN0KCkNCg0KZm9yIChwYXRpZW50IGluIHBhdGllbnRfbGlzdCkgew0KICAjIE5hbWUgb3V0cHV0IGZvbGRlcg0KICBvdXRfZGlyIDwtIHBhc3RlMChwYXRpZW50LCAiX0NOViIpDQogIGlmIChzdHJfZGV0ZWN0KHBhc3RlKHJlYWRMaW5lcyhwYXN0ZTAocGF0aWVudCwgIi5Bbm5vdGF0aW9ucy50c3YiKSksIGNvbGxhcHNlID0gJycpLCByZWZlcmVuY2UpID09IEZBTFNFKSB7DQogICAgZmFpbGVkIDwtIGMoZmFpbGVkLCBwYXRpZW50KQ0KICAgIG5leHQNCiAgICB9DQoNCiAgIyBDcmVhdGUgdGhlIGluZmVyY252IG9iamVjdA0KICBpbmZlcmNudl9vYmogPSBDcmVhdGVJbmZlcmNudk9iamVjdChyYXdfY291bnRzX21hdHJpeD0gcGFzdGUwKHBhdGllbnQsICIuQ291bnRzLnRzdiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uc19maWxlPSBwYXN0ZTAocGF0aWVudCwgIi5Bbm5vdGF0aW9ucy50c3YiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVsaW09Ilx0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9vcmRlcl9maWxlPSAiZ2VuZV9vcmRlcl9Ic19XVEFfdjFfcGtjLnR4dCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICNnZW5lX29yZGVyX2ZpbGU9ICJnZW5jb2RlX3YxOV9nZW5lX3Bvcy50eHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWZfZ3JvdXBfbmFtZXM9IHJlZmVyZW5jZSwgIyBpbnB1dCB0aGUgbm9ybWFsL3JlZmVyZW5jZSBncm91cCBuYW1lcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaHJfZXhjbHVkZSA9IGMoImNock0iKSkjYygiY2hyTSIpKSAjIERlZmF1bHQgZXhjbHVkZXMgY2hyWCwgY2hyWSBhbmQgY2hyTS4gQnkgb25seSBwaWNraW5nIGNock0geW91IGluY2x1ZGUgdGhlIFggYW5kIFkgY2hyb21vc29tZXMuDQoNCiAgIyBwZXJmb3JtIGluZmVyY252IG9wZXJhdGlvbnMgdG8gcmV2ZWFsIGNudiBzaWduYWwuIEZvciBhbGwgb3B0aW9uczogaHR0cHM6Ly9yZHJyLmlvL2dpdGh1Yi9icm9hZGluc3RpdHV0ZS9pbmZlcmNudi9tYW4vcnVuLmh0bWwNCiAgI2Nudl9saXN0W1twYXRpZW50XV0NCiAgaW5mZXJjbnZfb2JqIDwtIGluZmVyY252OjpydW4oaW5mZXJjbnZfb2JqLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1dG9mZj0wLjEsICAjIHVzZSAxIGZvciBzbWFydC1zZXEsIDAuMSBmb3IgMTB4LWdlbm9taWNzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3V0X2Rpcj0gb3V0X2RpciwgICMgZGlyIGlzIGF1dG8tY3JlYXRlZCBmb3Igc3RvcmluZyBvdXRwdXRzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9ieV9ncm91cHM9RkFMU0UsICAgIyBjbHVzdGVyDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVub2lzZT1UUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhNTT1GQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0dW1vcl9zdWJjbHVzdGVyX3BhcnRpdGlvbl9tZXRob2QgPSBjKCJxbm9ybSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuYWx5c2lzX21vZGUgPSAic3ViY2x1c3RlcnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vX3Bsb3Q9VFJVRQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMsZGVidWc9VFJVRQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCg0KICBwbG90X2NudihpbmZlcmNudl9vYmosDQogICAgICAgICAgb3V0X2RpciA9IHBhc3RlMChwYXRpZW50LCAiX0NOViIpLA0KICAgICAgICAgIHRpdGxlID0gImluZmVyQ05WIiwNCiAgICAgICAgICBvYnNfdGl0bGUgPSAiT2JzZXJ2YXRpb25zIChDZWxscykiLA0KICAgICAgICAgIHJlZl90aXRsZSA9ICJSZWZlcmVuY2VzIChDZWxscykiLA0KICAgICAgICAgIGNsdXN0ZXJfYnlfZ3JvdXBzID0gRkFMU0UsDQogICAgICAgICAgY2x1c3Rlcl9yZWZlcmVuY2VzID0gRkFMU0UsDQogICAgICAgICAgcGxvdF9jaHJfc2NhbGUgPSBGQUxTRSwNCiAgICAgICAgICAjY2hyX2xlbmd0aHMgPSBOVUxMLA0KICAgICAgICAgIGtfb2JzX2dyb3VwcyA9IDEsDQogICAgICAgICAgY29udGlnX2NleCA9IDEuNSwNCiAgICAgICAgICAjeC5jZW50ZXIgPSBtZWFuKGluZmVyY252X29iakBleHByLmRhdGEpLA0KICAgICAgICAgIHgucmFuZ2UgPSAiYXV0byIsDQogICAgICAgICAgI2hjbHVzdF9tZXRob2QgPSAid2FyZC5EIiwNCiAgICAgICAgICBvdXRwdXRfZmlsZW5hbWUgPSAiaW5mZXJjbnYiLA0KICAgICAgICAgIG91dHB1dF9mb3JtYXQgPSAicG5nIiwNCiAgICAgICAgICBwbmdfcmVzID0gMzAwDQogICAgICAgICAgKQ0KfQ0KYGBgDQoNCmBgYHtyIHNob3cgQ05WLCBmaWcuaGVpZ2h0PTMwLCBmaWcud2lkdGg9MjV9DQpmb3IgKHBhdGllbnQgaW4gcGF0aWVudF9saXN0KSB7DQogIHByaW50KHBhc3RlKGdyb3VwX2ZpbHRlciwgcGF0aWVudCkpDQogIGlmIChwYXRpZW50ICVpbiUgZmFpbGVkKSB7DQogICAgcHJpbnQoIiBeICAgIFBhdGllbnQgY29udGFpbmVkIG5vIHJlZmVyZW5jZSBncm91cCIpDQogICAgbmV4dA0KICB9DQogIGltZyA8LSByZWFkUE5HKHBhc3RlKHBhc3RlMChwYXRpZW50LCAiX0NOViIpLCAiL2luZmVyY252LnBuZyIsIHNlcCA9ICIiKSkNCiAgZ3JpZDo6Z3JpZC5uZXdwYWdlKCkNCiAgZ3JpZDo6Z3JpZC5yYXN0ZXIoaW1nKQ0KfQ0KYGBgDQoNCiMgMTEuMSBEZW5kcm9ncmFtDQoNCkNsb3NlciBsb29rIGF0IHRoZSBkZW5kcm9ncmFtIGZyb20gdGhlIENOViBhbmFseXNpcyBwZXIgcGF0aWVudCB3aGVyZSB0aGUgbm9kZXMgYXMgd3JpdHRlbiBudW1iZXJzLiBVc2UgdGhlIG51bWJlcnMgdG8gc2VsZWN0IHN1Ymdyb3VwcyBmb3IgZnVydGhlciBhbmFseXNpcyBpbiAndC10ZXN0IG9uIHR3byBzdWJncm91cHMnLg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MjB9DQojb3V0X2RpciA8LSAiVDFfTkFOT18wMTJfQ05WIg0KdHJlZXMgPC0gbGlzdCgpDQoNCmZvciAocGF0aWVudCBpbiBwYXRpZW50X2xpc3QpIHsNCiAgaWYgKHBhdGllbnQgJWluJSBmYWlsZWQpIHsNCiAgICAjcHJpbnQoIlBhdGllbnQgY29udGFpbmVkIG5vIHJlZmVyZW5jZSBncm91cCIpDQogICAgbmV4dA0KICB9DQp0cmVlIDwtIHJlYWQudHJlZShwYXN0ZShwYXRpZW50LCJfQ05WL2luZmVyY252Lm9ic2VydmF0aW9uc19kZW5kcm9ncmFtLnR4dCIsIHNlcCA9ICIiKSkNCm9idiA8LSByZWFkLmNzdihwYXN0ZShwYXRpZW50LCJfQ05WL2luZmVyY252Lm9ic2VydmF0aW9uX2dyb3VwaW5ncy50eHQiLCBzZXAgPSAiIiksIHNlcD0iIikNCnRyZWVzW1twYXRpZW50XV0gPC0gZ2d0cmVlKA0KICB0cmVlLCBsYWRkZXJpemU9RikgKw0KICBnZW9tX3RyZWVzY2FsZSgpICsNCiAgZ2VvbV90aXBsYWIoY29sb3I9b2J2JEFubm90YXRpb24uQ29sb3IsIGhqdXN0PS0uMikgKw0KICBjb29yZF9jYXJ0ZXNpYW4oY2xpcCA9ICdvZmYnKSArDQogIHRoZW1lX3RyZWUyKHBsb3QubWFyZ2luPW1hcmdpbig2LCAyMDAsIDYsIDYpKSArDQogIGdlb21fdGV4dDIoYWVzKGxhYmVsPW5vZGUpLCBoanVzdD0tLjMsIHNpemUgPSAzKSArDQogIGdncGxvdDI6OmxhYnModGl0bGUgPSBwYXRpZW50KQ0KfQ0KDQpncmlkLmFycmFuZ2UoZ3JvYnM9dHJlZXMsbmNvbD0zKQ0KYGBgDQoNCiMjIHQtdGVzdCBjaHIgd2l0aCBncm91cHMNCg0KQ29tcGFyZSB0aGUgY29udHJhc3Qgd2l0aGluIGEgY2hyb21vc29tZS4gU2VsZWN0IGEgY2hyb21vc29tZSwgY29udHJhc3QsIGFuZCBwYXRpZW50IG9mIGludGVyZXN0IHRvIHJ1biBhIHQtdGVzdCBvbi4NCg0KYGBge3J9DQojIyMjIyMjIyNQQVJBTUVURVJTIyMjIyMjIyMNCmNociA8LSAiY2hyWCIgIyBTZWxlY3QgY2hyb21vc29tZSBvZiBpbnRlcmVzdA0KY29udHJhc3QgPC0gYygibm9ybWFsIiwgIkRLRCIpICMgU2VsZWN0IGNvbnRyYXN0DQpwYXRpZW50IDwtICJkdW1teSINCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQpwb3NpdGlvbnMgPC0gYXMuZGF0YS5mcmFtZShwb3NpdGlvbnMpDQpjb2xuYW1lcyhwb3NpdGlvbnMpIDwtIGMoImdlbmUiLCAiY2hyIiwgImJlZ2luIiwgImVuZCIpDQpzZWxlY3RfZ2VuZXMgPC0gcG9zaXRpb25zJGdlbmVbcG9zaXRpb25zJGNociA9PSBjaHJdICMgR3JhYiBjaHIgc3BlY2lmaWMgZ2VuZXMNCg0KYW5ub3RhdGlvbiA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuZGVsaW0ocGFzdGUwKHBhdGllbnQsICIuQW5ub3RhdGlvbnMudHN2IiksIGhlYWRlcj1GQUxTRSkpDQpjb2xuYW1lcyhhbm5vdGF0aW9uKSA8LSBjKCJTYW1wbGVfSUQiLCAiQU5OIikNCnNlbGVjdF9zYW1wbGVzIDwtIGFubm90YXRpb24NCg0KZmlsdGVyX2NvdW50cyA8LSBhcy5kYXRhLmZyYW1lKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImxvZ19xIl1dKQ0KY29sbmFtZXMoZmlsdGVyX2NvdW50cykgPC0gZ3N1YignLmRjYycsJycsIGNvbG5hbWVzKGZpbHRlcl9jb3VudHMpKQ0KZmlsdGVyX2NvdW50cyA8LSBmaWx0ZXJfY291bnRzWyxzZWxlY3Rfc2FtcGxlcyRTYW1wbGVfSURdICMgZmlsdGVyIG91dCBzYW1wbGVzIHRoYXQgYXJlIG5vdCB0aGUgaW50ZXJlc3RpbmcgcmVnaW9uDQoNCnNlbGVjdF9nZW5lcyA8LSBzZWxlY3RfZ2VuZXNbc2VsZWN0X2dlbmVzICVpbiUgcm93bmFtZXMoZmlsdGVyX2NvdW50cyldICMgT25seSB1c2UgZ2VuZXMgdGhhdCBhcmUgYWN0dWFsbHkgaW4gdGhlIGRhdGEgKHBrYyBnZW5lIGZpbGUgaGFzIGFsbCBvZiB0aGVtKQ0KZmlsdGVyX2NvdW50cyA8LSBmaWx0ZXJfY291bnRzW3NlbGVjdF9nZW5lcyxdICMgZmlsdGVyIG91dCBub24gY2hyb21vc29tZSBzcGVjaWZpYyBnZW5lcw0KYGBgDQoNCmBgYHtyIHR0ZXN0IGNociwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTAgfQ0KcGxvdHM8LWxpc3QoKQ0KdGFibGVzPC1saXN0KCkNCmxhYmVsczwtbGlzdCgpDQp0ZXN0PC0idHRlc3QiDQptdGM8LSJCSCINCmNvdW50ZXI9MQ0KDQpsb2dfcV9maWx0ZXIgPC1hcy5kYXRhLmZyYW1lKGZpbHRlcl9jb3VudHMpDQoNCmNvbXBzX2RmPC1kYXRhLmZyYW1lKGNvbXA9JycsdmFsPScnKQ0KDQpmb3IgKGFjdGl2ZV9ncm91cDEgaW4gY29udHJhc3QpIHsNCiAgICBmb3IgKGFjdGl2ZV9ncm91cDIgaW4gY29udHJhc3QpIHsNCg0KICAgICAgI3N1cHJlc3MgcmVkdW5jYW50IGNvbXBhcmVzDQogICAgICBpZihhY3RpdmVfZ3JvdXAxPT1hY3RpdmVfZ3JvdXAyKSB7bmV4dH0NCiAgICAgIGNvbXA8LXBhc3RlKHNvcnQoYyhhY3RpdmVfZ3JvdXAxLGFjdGl2ZV9ncm91cDIpKSxjb2xsYXBzZSA9ICJfIikNCiAgICAgICNwcmludChjb21wKQ0KICAgICAgaWYoY29tcCAlaW4lIGNvbXBzX2RmJGNvbXApIHtuZXh0fQ0KICAgICAgdGVtcF9kZjwtZGF0YS5mcmFtZShjb21wPWNvbXAgLHZhbD0xKQ0KICAgICAgY29tcHNfZGY8LXJiaW5kKGNvbXBzX2RmLHRlbXBfZGYpDQoNCiAgICAgIGxhYmVsc1tbY291bnRlcl1dPC1wYXN0ZShhY3RpdmVfZ3JvdXAxLCIgdnMgIiwgYWN0aXZlX2dyb3VwMikNCiAgICAgIGdyb3VwMTwtbG9nX3FfZmlsdGVyWyxuYW1lcyhhcy5kYXRhLmZyYW1lKGZpbHRlcl9jb3VudHMpKVtzZWxlY3Rfc2FtcGxlcyRBTk49PWFjdGl2ZV9ncm91cDFdXQ0KICAgICAgZ3JvdXAyPC1sb2dfcV9maWx0ZXJbLG5hbWVzKGFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykpW3NlbGVjdF9zYW1wbGVzJEFOTj09YWN0aXZlX2dyb3VwMl1dDQoNCiAgICAgICNydW4gdF90ZXN0cw0KICAgICAgcmVzdWx0czwtYXMuZGF0YS5mcmFtZSAoIGFwcGx5KGxvZ19xX2ZpbHRlciwgMSwgZnVuY3Rpb24oeCkgdC50ZXN0KHhbY29sbmFtZXMoZ3JvdXAxKV0seFtjb2xuYW1lcyhncm91cDIpXSkkcC52YWx1ZSkgKQ0KICAgICAgY29sbmFtZXMocmVzdWx0cyk8LSJyYXdfcF92YWx1ZSINCg0KICAgICAgI211bHRpcGxlX3Rlc3RpbmdfY29ycmVjdGlvbg0KICAgICAgYWRqX3BfdmFsdWU8LSBwLmFkanVzdChyZXN1bHRzJHJhd19wX3ZhbHVlLG1ldGhvZD1tdGMpDQogICAgICByZXN1bHRzPC1jYmluZChyZXN1bHRzLGFkal9wX3ZhbHVlKQ0KDQogICAgICAjY2FsY19mZHINCiAgICAgIEZEUjwtIHAuYWRqdXN0KHJlc3VsdHMkcmF3X3BfdmFsdWUsbWV0aG9kPSJmZHIiKQ0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxGRFIpDQoNCiAgICAgICNmb2xkX2NoYW5nZXMNCiAgICAgICNhcyBiYXNlIGRhdGEgaXMgYWxyZWFkeSBsb2cgdHJhbnNmb3JtZWQsIG1lYW5zIG5lZWQgdG8gYmUgc3VidHJhY3RlZCB0byBnZXQgRkMgaW4gbG9nIHNwYWNlDQogICAgICBmY2hhbmdlczwtYXMuZGF0YS5mcmFtZSggYXBwbHkobG9nX3FfZmlsdGVyLCAxLCBmdW5jdGlvbih4KSAobWVhbih4W2NvbG5hbWVzKGdyb3VwMSldKSAtIG1lYW4oeFtjb2xuYW1lcyhncm91cDIpXSkgKSApICkNCiAgICAgIGNvbG5hbWVzKGZjaGFuZ2VzKTwtIkZDIg0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxmY2hhbmdlcykNCg0KICAgICAgI2FkZCBnZW5lbmFtZXMNCiAgICAgIHJlc3VsdHMkR2VuZTwtcm93bmFtZXMocmVzdWx0cykNCg0KICAgICAgI3NldCBjYXRlZ29yaWVzIGJhc2VkIG9uIFAtdmFsdWUgJiBGRFIgZm9yIHBsb3R0aW5nDQogICAgICByZXN1bHRzJENvbG9yIDwtICJOUyBvciBGQyA8IDAuNSINCiAgICAgIHJlc3VsdHMkQ29sb3JbcmVzdWx0cyRhZGpfcF92YWx1ZSA8IDAuMDVdIDwtICJQIDwgMC4wNSINCiAgICAgIHJlc3VsdHMkQ29sb3JbcmVzdWx0cyRGRFIgPCAwLjA1XSA8LSAiRkRSIDwgMC4wNSINCiAgICAgIHJlc3VsdHMkQ29sb3JbcmVzdWx0cyRGRFIgPCAwLjAwMV0gPC0gIkZEUiA8IDAuMDAxIg0KICAgICAgcmVzdWx0cyRDb2xvclthYnMocmVzdWx0cyRGQykgPCAwLjVdIDwtICJOUyBvciBGQyA8IDAuNSINCiAgICAgIHJlc3VsdHMkQ29sb3IgPC0gZmFjdG9yKHJlc3VsdHMkQ29sb3IsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJOUyBvciBGQyA8IDAuNSIsICJQIDwgMC4wNSIsICJGRFIgPCAwLjA1IiwgIkZEUiA8IDAuMDAxIikpDQoNCiAgICAgICN2dWxjYW5vcGxvdA0KDQogICAgICAjIHBpY2sgdG9wIGdlbmVzIGZvciBlaXRoZXIgc2lkZSBvZiB2b2xjYW5vIHRvIGxhYmVsDQogICAgICAjIG9yZGVyIGdlbmVzIGZvciBjb252ZW5pZW5jZToNCg0KICAgICAgcmVzdWx0cyRpbnZlcnRfUCA8LSAoLWxvZzEwKHJlc3VsdHMkYWRqX3BfdmFsdWUpKSAqIHNpZ24ocmVzdWx0cyRGQykNCiAgICAgIHRvcF9nIDwtIGMoKQ0KICAgICAgdG9wX2cgPC0gYyh0b3BfZywNCiAgICAgICAgICAgICAgICAgcmVzdWx0c1tpbmQsICdHZW5lJ11bDQogICAgICAgICAgICAgICAgICAgb3JkZXIocmVzdWx0c1tpbmQsICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gVFJVRSlbMToxNV1dLA0KICAgICAgICAgICAgICAgICByZXN1bHRzW2luZCwgJ0dlbmUnXVtvcmRlcihyZXN1bHRzW2luZCwgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToxNV1dKQ0KICAgICAgdG9wX2c8LSB1bmlxdWUodG9wX2cpDQogICAgICByZXN1bHRzIDwtIHJlc3VsdHNbLCAtMSpuY29sKHJlc3VsdHMpXSAjIHJlbW92ZSBpbnZlcnRfUCBmcm9tIG1hdHJpeA0KDQogICAgICAjIEdyYXBoIHJlc3VsdHMNCiAgICAgIHBsb3RzW1tjb3VudGVyXV08LSBnZ3Bsb3QocmVzdWx0cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBGQywgeSA9IC1sb2cxMChhZGpfcF92YWx1ZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IENvbG9yLCBsYWJlbCA9IEdlbmUpKSArDQogICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoMC41LCAtMC41KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgICBsYWJzKHggPSBwYXN0ZSgiRW5yaWNoZWQgZ2VuZXMgaW4iLCBjaHIsICItIiwgYWN0aXZlX2dyb3VwMiwiIDwtIGxvZzIoRkMpIC0+IEVucmljaGVkIGluIiwgYWN0aXZlX2dyb3VwMSksDQogICAgICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKw0KICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhgRkRSIDwgMC4wMDFgID0gImRvZGdlcmJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFAgPCAwLjA1YCA9ICJvcmFuZ2UyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYE5TIG9yIEZDIDwgMC41YCA9ICJncmF5IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgICAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHN1YnNldChyZXN1bHRzLCBGRFI8MC4wNSAmICgtMC41PkZDfCBGQz4wLjUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSAwLjE1LCBjb2xvciA9ICJibGFjayIsIHNpemU9My41LA0KICAgICAgICAgICAgICAgICAgICAgICAgbWluLnNlZ21lbnQubGVuZ3RoID0gLjEsIGJveC5wYWRkaW5nID0gLjIsIGx3ZCA9IDIsDQogICAgICAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSA1MCkgKw0KICAgICAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAyMCkgKw0KICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICAgICAgICAjZ2d0aXRsZShwYXN0ZShzbGlkZSwiOiAiLCB0ZXN0LCBtdGMsIm11bHRpdGVzdCBjb3JyIikpDQogICAgICAgIGdndGl0bGUocGFzdGUocGF0aWVudCwgIjogIiwgdGVzdCwgbXRjLCJtdWx0aXRlc3QgY29yciIpKQ0KDQogICAgICAjc3RvcmUgdGFibGVzIGZvciBkaXNwbGF5IGxhdGVyDQogICAgICB0YWJsZXNbW2NvdW50ZXJdXTwtcmVzdWx0cw0KDQogICAgICBjb3VudGVyID0gY291bnRlcisxDQogICAgICAjZGF0YXRhYmxlKHN1YnNldChyZXN1bHRzLCBHZW5lICVpbiUgR09JKSwgcm93bmFtZXM9RkFMU0UsY2FwdGlvbiA9IHBhc3RlKCJERSByZXN1bHRzICIsIGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKSkNCiAgICB9DQogIH0NCg0KZ3JpZC5hcnJhbmdlKGdyb2JzPXBsb3RzLG5jb2w9MikNCmBgYA0KDQojIHQtdGVzdCBvbiB0d28gc3ViZ3JvdXBzDQoNClNlbGVjdCBub2RlcyBmcm9tIHRoZSBkZW5kcm9ncmFtIGFzIHN1Ymdyb3VwcywgaW4gdGhpcyBjYXNlIG5vZGVzIDE4IGFuZCAxNS4gRGVjaWRlIG9uIHRoZSBpbnZvbHZlZCBjaHJvbW9zb21lcyBhbmQgYWRkIHRoZW0gaW50byB0aGUgY2hyX2xpc3QuIFNwZWNpZnkgdGhlIHBhdGllbnQgaW4gcGF0aWVudC4NCg0KYGBge3J9DQojIyMjIyMjIyNQQVJBTUVURVJTIyMjIyMjIyMNCmNocl9saXN0IDwtIGMoImNocjIyIikgIyBDaG9vc2UgbmVlZGVkIGNocm9tb3NvbWVzIG9mIHRoZSB0YXJnZXQgYXJlYS4gVGhpbmsgb2YgaXQgYXMgdGhlIHgtYXhpcw0KcGF0aWVudCA8LSAiZHVtbXkiDQpub2RlX2ludGVyZXN0IDwtIDIzMyAjIENob29zZSBzdWJncm91cCAxDQpub2RlX2ludGVyZXN0MiA8LSAyMTYgIyBDaG9vc2Ugc3ViZ3JvdXAgMg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCnRyZWUgPC0gcmVhZC50cmVlKHBhc3RlKHBhdGllbnQsIl9DTlYvaW5mZXJjbnYub2JzZXJ2YXRpb25zX2RlbmRyb2dyYW0udHh0Iiwgc2VwID0gIiIpKQ0KdHJlZV9pbmZvIDwtIHRyZWUgJT4lIGFzLnRyZWVkYXRhICU+JSBhc190aWJibGUgIyB0cmFuc2Zvcm0gdHJlZSBpbnRvIGFjY2Vzc2libGUgZGF0YQ0KDQpzYW1wbGVzX2ludGVyZXN0IDwtIG9mZnNwcmluZyh0cmVlX2luZm8sIG5vZGVfaW50ZXJlc3QpICMgR2V0IGxhYmVscyBhdHRhY2hlZCB0byB0aGUgZ3JvdXANCnNhbXBsZXNfaW50ZXJlc3QgPC0gYXMuY2hhcmFjdGVyKG5hLm9taXQoc2FtcGxlc19pbnRlcmVzdCRsYWJlbCkpICMgZm9ybWF0dGluZw0KcGFzdGUoIlN1Ymdyb3VwIDE6ICIsIHNhbXBsZXNfaW50ZXJlc3QpDQoNCnNhbXBsZXNfaW50ZXJlc3QyIDwtIG9mZnNwcmluZyh0cmVlX2luZm8sIG5vZGVfaW50ZXJlc3QyKQ0Kc2FtcGxlc19pbnRlcmVzdDIgPC0gYXMuY2hhcmFjdGVyKG5hLm9taXQoc2FtcGxlc19pbnRlcmVzdDIkbGFiZWwpKQ0KcGFzdGUoIlN1Ymdyb3VwIDI6ICIsIHNhbXBsZXNfaW50ZXJlc3QyKQ0KYGBgDQoNCmBgYHtyfQ0KcG9zaXRpb25zIDwtIGFzLmRhdGEuZnJhbWUocG9zaXRpb25zKQ0KY29sbmFtZXMocG9zaXRpb25zKSA8LSBjKCJnZW5lIiwgImNociIsICJiZWdpbiIsICJlbmQiKSAjIEZvcm1hdHRpbmcNCg0Kc2VsZWN0X2dlbmVzIDwtIHBvc2l0aW9ucyRnZW5lW3Bvc2l0aW9ucyRjaHIgJWluJSBjaHJfbGlzdF0gIyB8IHBvc2l0aW9ucyRjaHIgPT0gY2hyMl0gIyBHcmFiIGNocm9tb3NvbWUgc3BlY2lmaWMgZ2VuZXMNCg0KYW5ub3RhdGlvbiA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuZGVsaW0ocGFzdGUwKHBhdGllbnQsICIuQW5ub3RhdGlvbnMudHN2IiksIGhlYWRlcj1GQUxTRSkpDQpjb2xuYW1lcyhhbm5vdGF0aW9uKSA8LSBjKCJTYW1wbGVfSUQiLCAiQU5OIikNCnNlbGVjdF9zYW1wbGVzIDwtIGFubm90YXRpb25bYW5ub3RhdGlvbiRTYW1wbGVfSUQgJWluJSBzYW1wbGVzX2ludGVyZXN0IHwgYW5ub3RhdGlvbiRTYW1wbGVfSUQgJWluJSBzYW1wbGVzX2ludGVyZXN0MixdICMgR3JhYiBzdWJncm91cCBzcGVjaWZpYyBTYW1wbGUgSURzDQoNCmZpbHRlcl9jb3VudHMgPC0gdGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1sibG9nX3EiXV0NCmNvbG5hbWVzKGZpbHRlcl9jb3VudHMpIDwtIGdzdWIoJy5kY2MnLCcnLCBjb2xuYW1lcyhmaWx0ZXJfY291bnRzKSkNCmZpbHRlcl9jb3VudHMgPC0gZmlsdGVyX2NvdW50c1ssc2VsZWN0X3NhbXBsZXMkU2FtcGxlX0lEXSAjIEZpbHRlciBTYW1wbGUgSUQncw0KDQpzZWxlY3RfZ2VuZXMgPC0gc2VsZWN0X2dlbmVzW3NlbGVjdF9nZW5lcyAlaW4lIHJvd25hbWVzKGZpbHRlcl9jb3VudHMpXSAjIE9ubHkgdXNlIGdlbmVzIHRoYXQgYXJlIGFjdHVhbGx5IGluIHRoZSBkYXRhIChwa2MgZ2VuZSBmaWxlIGhhcyBhbGwgb2YgdGhlbSkNCmZpbHRlcl9jb3VudHMgPC0gZmlsdGVyX2NvdW50c1tzZWxlY3RfZ2VuZXMsXSAjIEZpbHRlciBnZW5lcw0KYGBgDQoNCmBgYHtyIHR0ZXN0IHN1Ymdyb3VwcywgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTB9DQpwbG90czwtbGlzdCgpDQp0YWJsZXM8LWxpc3QoKQ0KbGFiZWxzPC1saXN0KCkNCnRlc3Q8LSJ0dGVzdCINCm10YzwtIkJIIg0KY291bnRlcj0xDQoNCmxvZ19xX2ZpbHRlciA8LWFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykNCg0KY29tcHNfZGY8LWRhdGEuZnJhbWUoY29tcD0nJyx2YWw9JycpDQoNCg0KYWN0aXZlX2dyb3VwMSA8LSBzYW1wbGVzX2ludGVyZXN0ICNzdWJncm91cCAxDQphY3RpdmVfZ3JvdXAyIDwtIHNhbXBsZXNfaW50ZXJlc3QyICNzdWJncm91cCAyDQoNCiMgZm9yIChhY3RpdmVfZ3JvdXAxIGluIGMoInN1YjEiKSkgew0KIyAgICAgZm9yIChhY3RpdmVfZ3JvdXAyIGluIGMoInN1YjIiKSkgew0KDQogICAgICAjc3VwcmVzcyByZWR1bmNhbnQgY29tcGFyZXMNCiAgICAgICNpZihhY3RpdmVfZ3JvdXAxPT1hY3RpdmVfZ3JvdXAyKSB7bmV4dH0NCiAgICAgICNjb21wPC1wYXN0ZShzb3J0KGMoYWN0aXZlX2dyb3VwMSxhY3RpdmVfZ3JvdXAyKSksY29sbGFwc2UgPSAiXyIpDQogICAgICAjcHJpbnQoY29tcCkNCiAgICAgICNpZihjb21wICVpbiUgY29tcHNfZGYkY29tcCkge25leHR9DQogICAgICB0ZW1wX2RmPC1kYXRhLmZyYW1lKGNvbXA9Y29tcCAsdmFsPTEpDQogICAgICBjb21wc19kZjwtcmJpbmQoY29tcHNfZGYsdGVtcF9kZikNCg0KICAgICAgIyBsYWJlbHNbW2NvdW50ZXJdXTwtcGFzdGUoYWN0aXZlX2dyb3VwMSwiIHZzICIsIGFjdGl2ZV9ncm91cDIpDQogICAgICAjIGdyb3VwMTwtbG9nX3FfZmlsdGVyWyxuYW1lcyhhcy5kYXRhLmZyYW1lKGZpbHRlcl9jb3VudHMpKVtzZWxlY3Rfc2FtcGxlcyRBTk49PWFjdGl2ZV9ncm91cDFdXQ0KICAgICAgIyBncm91cDI8LWxvZ19xX2ZpbHRlclssbmFtZXMoYXMuZGF0YS5mcmFtZShmaWx0ZXJfY291bnRzKSlbc2VsZWN0X3NhbXBsZXMkQU5OPT1hY3RpdmVfZ3JvdXAyXV0NCg0KICAgICAgbGFiZWxzW1tjb3VudGVyXV08LXBhc3RlKGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKQ0KICAgICAgZ3JvdXAxPC1sb2dfcV9maWx0ZXJbLG5hbWVzKGFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykpW3NlbGVjdF9zYW1wbGVzJFNhbXBsZV9JRCAlaW4lIGFjdGl2ZV9ncm91cDFdXQ0KICAgICAgZ3JvdXAyPC1sb2dfcV9maWx0ZXJbLG5hbWVzKGFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykpW3NlbGVjdF9zYW1wbGVzJFNhbXBsZV9JRCAlaW4lIGFjdGl2ZV9ncm91cDJdXQ0KDQogICAgICAjcnVuIHRfdGVzdHMNCiAgICAgIHJlc3VsdHM8LWFzLmRhdGEuZnJhbWUgKCBhcHBseShsb2dfcV9maWx0ZXIsIDEsIGZ1bmN0aW9uKHgpIHQudGVzdCh4W2NvbG5hbWVzKGdyb3VwMSldLHhbY29sbmFtZXMoZ3JvdXAyKV0pJHAudmFsdWUpICkNCiAgICAgIGNvbG5hbWVzKHJlc3VsdHMpPC0icmF3X3BfdmFsdWUiDQoNCiAgICAgICNtdWx0aXBsZV90ZXN0aW5nX2NvcnJlY3Rpb24NCiAgICAgIGFkal9wX3ZhbHVlPC0gcC5hZGp1c3QocmVzdWx0cyRyYXdfcF92YWx1ZSxtZXRob2Q9bXRjKQ0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxhZGpfcF92YWx1ZSkNCg0KICAgICAgI2NhbGNfZmRyDQogICAgICBGRFI8LSBwLmFkanVzdChyZXN1bHRzJHJhd19wX3ZhbHVlLG1ldGhvZD0iZmRyIikNCiAgICAgIHJlc3VsdHM8LWNiaW5kKHJlc3VsdHMsRkRSKQ0KDQogICAgICAjZm9sZF9jaGFuZ2VzDQogICAgICAjYXMgYmFzZSBkYXRhIGlzIGFscmVhZHkgbG9nIHRyYW5zZm9ybWVkLCBtZWFucyBuZWVkIHRvIGJlIHN1YnRyYWN0ZWQgdG8gZ2V0IEZDIGluIGxvZyBzcGFjZQ0KICAgICAgZmNoYW5nZXM8LWFzLmRhdGEuZnJhbWUoIGFwcGx5KGxvZ19xX2ZpbHRlciwgMSwgZnVuY3Rpb24oeCkgKG1lYW4oeFtjb2xuYW1lcyhncm91cDEpXSkgLSBtZWFuKHhbY29sbmFtZXMoZ3JvdXAyKV0pICkgKSApDQogICAgICBjb2xuYW1lcyhmY2hhbmdlcyk8LSJGQyINCiAgICAgIHJlc3VsdHM8LWNiaW5kKHJlc3VsdHMsZmNoYW5nZXMpDQoNCiAgICAgICNhZGQgZ2VuZW5hbWVzDQogICAgICByZXN1bHRzJEdlbmU8LXJvd25hbWVzKHJlc3VsdHMpDQoNCiAgICAgICNzZXQgY2F0ZWdvcmllcyBiYXNlZCBvbiBQLXZhbHVlICYgRkRSIGZvciBwbG90dGluZw0KICAgICAgcmVzdWx0cyRDb2xvciA8LSAiTlMgb3IgRkMgPCAwLjUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkYWRqX3BfdmFsdWUgPCAwLjA1XSA8LSAiUCA8IDAuMDUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkRkRSIDwgMC4wNV0gPC0gIkZEUiA8IDAuMDUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkRkRSIDwgMC4wMDFdIDwtICJGRFIgPCAwLjAwMSINCiAgICAgIHJlc3VsdHMkQ29sb3JbYWJzKHJlc3VsdHMkRkMpIDwgMC41XSA8LSAiTlMgb3IgRkMgPCAwLjUiDQogICAgICByZXN1bHRzJENvbG9yIDwtIGZhY3RvcihyZXN1bHRzJENvbG9yLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiTlMgb3IgRkMgPCAwLjUiLCAiUCA8IDAuMDUiLCAiRkRSIDwgMC4wNSIsICJGRFIgPCAwLjAwMSIpKQ0KDQogICAgICAjdnVsY2Fub3Bsb3QNCg0KICAgICAgIyBwaWNrIHRvcCBnZW5lcyBmb3IgZWl0aGVyIHNpZGUgb2Ygdm9sY2FubyB0byBsYWJlbA0KICAgICAgIyBvcmRlciBnZW5lcyBmb3IgY29udmVuaWVuY2U6DQoNCiAgICAgIHJlc3VsdHMkaW52ZXJ0X1AgPC0gKC1sb2cxMChyZXN1bHRzJGFkal9wX3ZhbHVlKSkgKiBzaWduKHJlc3VsdHMkRkMpDQogICAgICB0b3BfZyA8LSBjKCkNCiAgICAgIHRvcF9nIDwtIGModG9wX2csDQogICAgICAgICAgICAgICAgIHJlc3VsdHNbaW5kLCAnR2VuZSddWw0KICAgICAgICAgICAgICAgICAgIG9yZGVyKHJlc3VsdHNbaW5kLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MTVdXSwNCiAgICAgICAgICAgICAgICAgcmVzdWx0c1tpbmQsICdHZW5lJ11bb3JkZXIocmVzdWx0c1tpbmQsICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gRkFMU0UpWzE6MTVdXSkNCiAgICAgIHRvcF9nPC0gdW5pcXVlKHRvcF9nKQ0KICAgICAgcmVzdWx0cyA8LSByZXN1bHRzWywgLTEqbmNvbChyZXN1bHRzKV0gIyByZW1vdmUgaW52ZXJ0X1AgZnJvbSBtYXRyaXgNCg0KICAgICAgIyBHcmFwaCByZXN1bHRzDQogICAgICAjcGxvdHNbW2NvdW50ZXJdXTwtIGdncGxvdChyZXN1bHRzLA0KICAgICAgcCA8LSBnZ3Bsb3QocmVzdWx0cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBGQywgeSA9IC1sb2cxMChhZGpfcF92YWx1ZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IENvbG9yLCBsYWJlbCA9IEdlbmUpKSArDQogICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoMC41LCAtMC41KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgICBsYWJzKHggPSBwYXN0ZSgiRW5yaWNoZWQgZ2VuZXMgZnJvbSIsIGNocl9saXN0LCAiaW4iLCAic3ViZ3JvdXAgMiIsIiA8LSBsb2cyKEZDKSAtPiBFbnJpY2hlZCBpbiIsICJzdWJncm91cCAxIiksDQogICAgICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKw0KICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhgRkRSIDwgMC4wMDFgID0gImRvZGdlcmJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFAgPCAwLjA1YCA9ICJvcmFuZ2UyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYE5TIG9yIEZDIDwgMC41YCA9ICJncmF5IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgICAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHN1YnNldChyZXN1bHRzLCBGRFI8MC4wNSAmICgtMC41PkZDfCBGQz4wLjUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSAwLjE1LCBjb2xvciA9ICJibGFjayIsIHNpemU9My41LA0KICAgICAgICAgICAgICAgICAgICAgICAgbWluLnNlZ21lbnQubGVuZ3RoID0gLjEsIGJveC5wYWRkaW5nID0gLjIsIGx3ZCA9IDIsDQogICAgICAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSA1MCkgKw0KICAgICAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAyMCkgKw0KICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICAgICAgICAjZ2d0aXRsZShwYXN0ZShzbGlkZSwiOiAiLCB0ZXN0LCBtdGMsIm11bHRpdGVzdCBjb3JyIikpDQogICAgICAgIGdndGl0bGUocGFzdGUocGF0aWVudCwgIjogIiwgdGVzdCwgbXRjLCJtdWx0aXRlc3QgY29yciIpKQ0KDQogICAgICAjc3RvcmUgdGFibGVzIGZvciBkaXNwbGF5IGxhdGVyDQogICAgICB0YWJsZXNbW2NvdW50ZXJdXTwtcmVzdWx0cw0KDQogICAgICBjb3VudGVyID0gY291bnRlcisxDQogICAgICAjZGF0YXRhYmxlKHN1YnNldChyZXN1bHRzLCBHZW5lICVpbiUgR09JKSwgcm93bmFtZXM9RkFMU0UsY2FwdGlvbiA9IHBhc3RlKCJERSByZXN1bHRzICIsIGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKSkNCiAgIyAgIH0NCiAgIyB9DQoNCiNnZ3Bsb3RseShwKQ0KcA0KI2dyaWQuYXJyYW5nZShncm9icz1wbG90cyxuY29sPTIpDQpgYGANCg0KIyAxMiBDb2RlICYgVmVyc2lvbnMNCg0KUGlwZWxpbmV2ZXJzaW9uOiB2MSBiYXNlZCBvbjoNCjxodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvd29ya2Zsb3dzL3ZpZ25ldHRlcy9HZW9NeFdvcmtmbG93cy9pbnN0L2RvYy9HZW9teFRvb2xzX1JOQS1OR1NfQW5hbHlzaXMuaHRtbD4NCg0KVGhlIHVuZGVybHlpbmcgY29kZSBjYW4gYmUgZG93bmxvYWRlZCBmcm9tIHRoZSAnQ29kZScsIGJ1dHRvbiBvbiB0aGUgdG9wDQpvZiB0aGlzIHBhZ2UuIENob29zZSBvcHRpb24gJ2Rvd25sb2FkIFJtZCcgdG8gZG93bmxvYWQgdGhlIGZ1bGwgcGlwZWxpbmUNCndoaWNoIGNhbiBiZSBvcGVuZWQgaW4gUiBvciBSc3R1ZGlvLiBTb21lIGZpbGVwYXRocyBhcmUgaGFyZGNvZGVkIGFuZA0KbmVlZCB0byBiZSBjaGFuZ2VkIGFjY29yZGluZyB0byB5b3VyIHNldHVwLg0KDQojIDEyLjEgUiBzZXNzaW9uIGluZm9ybWF0aW9uDQoNCmBgYHtyIHNlc3Npb25faW5mb30NCnNlc3Npb25JbmZvKCkNCmBgYA0KDQojIyAxMi4yIFJlZmVyZW5jZXMNCg0KIVtdKGh0dHA6Ly91c2VxLm5sL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDIyLzEyL2RlY29yYXRpb24tc3Ryb2tlLWZsYXQucG5nKQ0KDQpgYGB7cn0NCmtuaXRyOjprbml0X2V4aXQoKQ0KYGBgDQoNCiMgOS4wIHJlbG9hZCBkYXRhDQoNCmBgYHtyIHNwYXRpYWxfZGVjb25fcHJlcGFyZX0NCiNyZWxvYWQgZGF0YQ0KIyBEYXRhIDwtDQojICAgcmVhZE5hbm9TdHJpbmdHZW9NeFNldChkY2NGaWxlcyA9IERDQ0ZpbGVzLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgcGtjRmlsZXMgPSBQS0NGaWxlcywNCiMgICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YUZpbGUgPSBTYW1wbGVBbm5vdGF0aW9uRmlsZSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YVNoZWV0ID0gIlNoZWV0MSIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGFEY2NDb2xOYW1lID0gIlNhbXBsZV9JRCIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICBwcm90b2NvbERhdGFDb2xOYW1lcyA9IGMoImFvaSIsICJyb2kiKSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cGVyaW1lbnREYXRhQ29sTmFtZXMgPSBjKCJwYW5lbCIpKQ0KIyANCiMgcGtjcyA8LSBhbm5vdGF0aW9uKERhdGEpDQojIG1vZHVsZXMgPC0gZ3N1YigiLnBrYyIsICIiLCBwa2NzKQ0KIyANCiMgI3NoaWZ0IGFueSBleHByZXNzaW9uIGNvdW50cyB3aXRoIGEgdmFsdWUgb2YgMCB0byAxIHRvIGVuYWJsZSBpbiBkb3duc3RyZWFtIHRyYW5zZm9ybWF0aW9ucy4NCiMgRGF0YSA8LSBzaGlmdENvdW50c09uZShEYXRhLCB1c2VEQUxvZ2ljID0gVFJVRSkNCiMgDQojICNjb2xsYXBzX3RhcmdldHMNCiMgdGFyZ2V0X0RhdGEgPC0gYWdncmVnYXRlQ291bnRzKERhdGEpDQojIGRpbSh0YXJnZXRfRGF0YSkNCiMgDQojICNub3JtYWxpemUNCiMgdGFyZ2V0X0RhdGEgPC0gbm9ybWFsaXplKHRhcmdldF9EYXRhICwgZGF0YV90eXBlID0gIlJOQSIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9tZXRob2QgPSAicXVhbnQiLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpcmVkUXVhbnRpbGUgPSAuNzUsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9FbHQgPSAicV9ub3JtIikNCmBgYA0KDQojIDkuNCBSdW4gYWR2YW5jZWQgc3BhdGlhbCBkZXZvbmNvbHV0aW9uDQoNCmBgYHtyfQ0KIyB2ZWN0b3IgaWRlbnRpZnlpbmcgcHVyZSB0dW1vciBzZWdtZW50czoNCnRhcmdldF9EYXRhJGlzdHVtb3IgPSB0YXJnZXRfRGF0YSRBTk4yID09ICJFcGl0aGVsaXVtIg0KDQojIHJ1biBzcGF0aWFsZGVjb24gd2l0aCBhbGwgdGhlIGJlbGxzIGFuZCB3aGlzdGxlczoNCnJlc3RpbHMgPSBydW5zcGF0aWFsZGVjb24ob2JqZWN0ID0gdGFyZ2V0X0RhdGEsDQogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZWx0ID0gInFfbm9ybSIsICAgICAgICAgICAgICAgICAgICAjIG5vcm1hbGl6ZWQgZGF0YQ0KICAgICAgICAgICAgICAgICAgICAgICAgICByYXdfZWx0ID0gImV4cHJzIiwgICAgICAgICAgICAgICAgICAgICAgIyBleHBlY3RlZCBiYWNrZ3JvdW5kIGNvdW50cyBmb3IgZXZlcnkgZGF0YSBwb2ludCBpbiBub3JtDQogICAgICAgICAgICAgICAgICAgICAgICAgIFggPSBzYWZlVE1FLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHNhZmVUTUUgbWF0cml4LCB1c2VkIGJ5IGRlZmF1bHQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY2VsbG1lcmdlcyA9IHNhZmVUTUUubWF0Y2hlcywgICAgICAgICAgICMgc2FmZVRNRS5tYXRjaGVzIG9iamVjdCwgdXNlZCBieSBkZWZhdWx0DQogICAgICAgICAgICAgICAgICAgICAgICAgIGNlbGxfY291bnRzID0gdGFyZ2V0X0RhdGEkbnVjbGVpLCAgICAgICMgbnVjbGVpIGNvdW50cywgdXNlZCB0byBlc3RpbWF0ZSB0b3RhbCBjZWxscw0KICAgICAgICAgICAgICAgICAgICAgICAgICBpc19wdXJlX3R1bW9yID0gdGFyZ2V0X0RhdGEkaXN0dW1vciwgICAjIGlkZW50aXRpZXMgb2YgdGhlIFR1bW9yIHNlZ21lbnRzL29ic2VydmF0aW9ucw0KICAgICAgICAgICAgICAgICAgICAgICAgICBuX3R1bW9yX2NsdXN0ZXJzID0gNSkgICAgICAgICAgICAgICAgICAgIyBob3cgbWFueSBkaXN0aW5jdCB0dW1vciBwcm9maWxlcyB0byBhcHBlbmQgdG8gc2FmZVRNRQ0KDQojc3RyKHBEYXRhKHJlc3RpbHMpKQ0KaGVhdG1hcChzd2VlcChyZXN0aWxzQGV4cGVyaW1lbnREYXRhQG90aGVyJFNwYXRpYWxEZWNvbk1hdHJpeCwgMSwgYXBwbHkocmVzdGlsc0BleHBlcmltZW50RGF0YUBvdGhlciRTcGF0aWFsRGVjb25NYXRyaXgsIDEsIG1heCksICIvIiksDQogICAgICAgICBsYWJSb3cgPSBOQSwgbWFyZ2lucyA9IGMoMTAsIDUpKQ0KDQpgYGANCg0KPCEtLSAjIDkuMy4yIFBsb3R0aW5nIGRlY29udm9sdXRpb24gcmVzdWx0cyAtLT4NCg0KPCEtLSBgYGB7ciwgZmlnLndpZHRoPTE1LGZpZy5oZWlnaHQ9N30gLS0+DQoNCjwhLS0gIyBGb3IgcmVmZXJlbmNlLCBzaG93IHRoZSBUSUxzIGNvbG9yIGRhdGEgb2JqZWN0IHVzZWQgYnkgdGhlIHBsb3R0aW5nIGZ1bmN0aW9ucyAgLS0+DQoNCjwhLS0gIyB3aGVuIHNhZmVUTUUgaGFzIGJlZW4gdXNlZDogLS0+DQoNCjwhLS0gZGF0YSgiY2VsbGNvbHMiKSAtLT4NCg0KPCEtLSAjY2VsbGNvbHMgLS0+DQoNCjwhLS0gbyA9IGhjbHVzdChkaXN0KHQocmVzJGNlbGwuY291bnRzJGNlbGwuY291bnRzKSkpJG9yZGVyIC0tPg0KDQo8IS0tIGxheW91dChtYXRyaXgoYygxLCAyKSwgMSksIHdpZHRocyA9IGMoNywgMykpIC0tPg0KDQo8IS0tIFRJTF9iYXJwbG90KHQocmVzJGNlbGwuY291bnRzJGNlbGwuY291bnRzWywgb10pLCAtLT4NCg0KPCEtLSAgICAgICAgICAgICBob3JpeiA9IFRSVUUsIGRyYXdfbGVnZW5kID0gVFJVRSwgY2V4Lm5hbWVzID0gMC45KSAtLT4NCg0KPCEtLSAjcGFyKG1hcj1jKDIsIDE1LCAyLCAyKSkgLS0+DQoNCjwhLS0gIyBvciB0aGUgcHJvcG9ydGlvbnMgb2YgY2VsbHM6IC0tPg0KDQo8IS0tIHRlbXAgPSByZXBsYWNlKHJlcyRwcm9wX29mX25vbnR1bW9yLCBpcy5uYShyZXMkcHJvcF9vZl9ub250dW1vciksIDApIC0tPg0KDQo8IS0tIG8gPSBoY2x1c3QoZGlzdCh0ZW1wW3JlcyRBTk4yID09ICJDRDQ1IixdKSkkb3JkZXIgLS0+DQoNCjwhLS0gVElMX2JhcnBsb3QodChyZXMkcHJvcF9vZl9hbGwpLCAgLS0+DQoNCjwhLS0gICAgICAgICAgICAgaG9yaXogPSBUUlVFLCBkcmF3X2xlZ2VuZCA9IFRSVUUsIGNleC5uYW1lcyA9IDAuOSkgLS0+DQoNCjwhLS0gYGBgIC0tPg0KDQojIDkuMy4zIEZsb3JldHMgb2YgU3BhdGlhbCBkZWNvbnZvbHV0aW9uDQoNClRoZSBzZWNvbmQgZnVuY3Rpb24gaXMgImZsb3JldHMiLCB1c2VkIGZvciBwbG90dGluZyBjZWxsIGFidW5kYW5jZXMgYXRvcA0Kc29tZSAyLUQgcHJvamVjdGlvbi4gSGVyZSwgd2UnbGwgcGxvdCBjZWxsIGFidW5kYW5jZXMgYXRvcCB0aGUgZmlyc3QgMg0KcHJpbmNpcGFsIGNvbXBvbmVudHMgb2YgdGhlIGRhdGE6DQoNCmBgYHtyfQ0KeHkgPC0gcmVhZF9leGNlbCgiTDovcGtsb29zdGVybWFuL0tpZG5leV9vcmdhbl9hdGxhcy9hbm5vdGF0aW9uL09yZ2FuQXRsYXNfS2lkbmV5Lnhsc3giLCBzaGVldCA9ICJTZWdtZW50UHJvcGVydGllcyIpDQp4eSRuZXd4IDwtICh4eSRST0lDb29yZGluYXRlWCAtIHh5JFNjYW5PZmZzZXRYKQ0KeHkkbmV3eSA8LSAoeHkkUk9JQ29vcmRpbmF0ZVkgLSB4eSRTY2FuT2Zmc2V0WSkNCnh5IDwtIHh5WyxjKCJTZWdtZW50RGlzcGxheU5hbWUiLCAibmV3eCIsICJuZXd5IildDQp4eVsiYW9pIl0gPC0geHlbIlNlZ21lbnREaXNwbGF5TmFtZSJdDQphbm5vdCA8LSB0YXJnZXRfRGF0YUBwcm90b2NvbERhdGFAZGF0YQ0KYW5ub3QgPC0gYW5ub3QgJT4lIGlubmVyX2pvaW4oeHksIGJ5ID0gJ2FvaScpICMlPiUgc2VsZWN0KERlc2NyaXB0aW9uLCBJRCwgZ2VuZUlELCBsZWFkaW5nRWRnZSwgcGFkaikNCmFubm90JHggPC0gYW5ub3QkbmV3eA0KYW5ub3QkeSA8LSBhbm5vdCRuZXd5DQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD0yNSxmaWcuaGVpZ2h0PTE1LCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjUwJSJ9DQojUDM2ID0gY3JlYXRlUGFsZXR0ZShsZW5ndGgocm93bmFtZXModChyZXMkYmV0YSkpKSwgIGMoIiNmZjAwMDAiLCAiIzAwZmYwMCIsICIjMDAwMGZmIikpDQoNCnhsaW0gPC0gbWF4KHJlc0BwaGVub0RhdGFAZGF0YSR4KQ0KeWxpbSA8LSBtYXgocmVzQHBoZW5vRGF0YUBkYXRhJHkpDQoNCmluZCA8LSBwRGF0YShyZXMpJHNsaWRlX25hbWUgPT0gImh1X2tpZG5leV8wMDEiDQpyZXMyIDwtIHJlc1ssaW5kXQ0KDQojYSA8LSByZXMyWyFyZXMyQHBoZW5vRGF0YUBkYXRhW1siYmV0YSJdXT09MF0NCg0KI3NsaWRlX2xpc3RbWzRdXSA8LSBmbGlwWShzbGlkZV9saXN0W1s0XV0pDQoNCmZsdW9yIDwtIGZsdW9yTGVnZW5kKG92ZXJsYXkgPSBzbGlkZV9saXN0W1s0XV0sIG5yb3cgPSA0LCB0ZXh0U2l6ZSA9IDUsIGFscGhhID0gMC4yNSkNCg0KaW0gPC0gcGxvdFNwYXRpYWxPdmVybGF5KG92ZXJsYXkgPSBzbGlkZV9saXN0W1s0XV0sIGxlZ2VuZCA9IEZBTFNFLCBoaVJlcyA9IEZBTFNFLCBjb3JuZXIgPSAiYm90dG9tcmlnaHQiLCANCiAgICAgICAgICAgICAgICAgICBzY2FsZUJhcldpZHRoID0gMC41LCB0ZXh0RGlzdGFuY2UgPSA1LCBzY2FsZUJhckNvbG9yID0gIndoaXRlIiwgaW1hZ2UgPSBUUlVFKSArIA0KICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gcGFzdGUoIldob2xlIHNsaWRlIE9NRS1USUZGIiwgc2xpZGVOYW1lKHNsaWRlX2xpc3RbWzRdXSkpKQ0KDQojcGxvdCgwLCB4bGltID0gYygwLCB4bGltKzQwMDAwKSwgeWxpbSA9IGMoMCwgeWxpbSksIGF4ZXMgPSBGQUxTRSkNCg0KY293cGxvdDo6Z2dkcmF3KCkgKw0KICBjb3dwbG90OjpkcmF3X3Bsb3QoaW0sIHggPSAtMC4zLCB5ID0gMCkgKw0KICBjb3dwbG90OjpkcmF3X3Bsb3QoZmx1b3IsIHNjYWxlID0gMC4xMiwgeCA9IDAuMSwgeSA9IDApICMrICMsIHNjYWxlID0gMC4xMiwgeCA9IC0wLjMsIHkgPSAtMC4yNSkNCg0KDQpmbG9yZXRzKA0KICB4ID0gcmVzMkBwaGVub0RhdGFAZGF0YSR4LCMgKyA0MDAwMCwNCiAgeSA9IHJlczJAcGhlbm9EYXRhQGRhdGEkeSwNCiAgY29sID0gUDM2LA0KICAjY29sID0gMTpsZW5ndGgocm93bmFtZXModChhJGJldGEpKSksDQogIHhsaW0gPSBjKDAseGxpbSs0MDAwMCksDQogIHlsaW0gPSBjKDAseWxpbSksDQogIGIgPSB0KGEkYmV0YSksDQogIHJlc2NhbGUuYnkuc3FydCA9IFRSVUUsDQogIGFkZCA9IEZBTFNFLA0KICBjZXggPSAxLCB4bGFiID0gIiIsIHlsYWIgPSAiIikNCg0KbGVnZW5kKCJyaWdodCIsDQogICAgICAgI2ZpbGwgPSAxOmxlbmd0aChyb3duYW1lcyh0KGEkYmV0YSkpKSwNCiAgICAgICBmaWxsID0gUDM2LA0KICAgICAgIGxlZ2VuZCA9IHJvd25hbWVzKHQoYSRiZXRhKSksIGNleCA9IDEuNSkNCmBgYA0KDQojIDkuNCBjb21iaW5pbmcgY2VsbHR5cGVzDQoNCldoZW4gdHdvIGNlbGwgdHlwZXMgYXJlIHRvbyBzaW1pbGFyLCB0aGUgZXN0aW1hdGlvbiBvZiB0aGVpciBhYnVuZGFuY2VzDQpiZWNvbWVzIHVuc3RhYmxlLiBIb3dldmVyLCB0aGVpciBzdW0gY2FuIHN0aWxsIGJlIGVzdGltYXRlZCBlYXNpbHkuIFRoZQ0KZnVuY3Rpb24gImNvbGxhcHNlQ2VsbFR5cGVzIiB0YWtlcyBhIGRlY29udm9sdXRpb24gcmVzdWx0cyBvYmplY3QgYW5kDQpjb2xsYXBzZXMgYW55IGNvbHNlbHktcmVsYXRlZCBjZWxsIHR5cGVzIHlvdSB0ZWxsIGl0IHRvDQoNCmBgYHtyfQ0KbWF0Y2hpbmcgPSBsaXN0KCkNCm1hdGNoaW5nJG15ZWxvaWQgPSBjKCAibWFjcm9waGFnZXMiLCAibW9ub2N5dGVzIiwgIm1EQ3MiKQ0KbWF0Y2hpbmckVC5OSyA9IGMoIkNENC5ULmNlbGxzIiwiQ0Q4LlQuY2VsbHMiLCAiVHJlZyIsICJOSyIpDQptYXRjaGluZyRCID0gYygiQiIpDQptYXRjaGluZyRtYXN0ID0gYygibWFzdCIpDQptYXRjaGluZyRuZXV0cm9waGlscyA9IGMoIm5ldXRyb3BoaWxzIikNCm1hdGNoaW5nJHN0cm9tYSA9IGMoImVuZG90aGVsaWFsLmNlbGxzIiwgImZpYnJvYmxhc3RzIikNCg0KY29sbGFwc2VkID0gY29sbGFwc2VDZWxsVHlwZXMoZml0ID0gcmVzdGlscywgbWF0Y2hpbmcgPSBtYXRjaGluZykNCg0KaGVhdG1hcChjb2xsYXBzZWQkYmV0YSwgY2V4Um93ID0gMC44NSwgY2V4Q29sID0gMC43NSkNCmBgYA0KDQojIExpZ2FuZC1yZWNlcHRvciBhbmFseXNlDQoNCmBgYHtyfQ0KI2RldnRvb2xzOjppbnN0YWxsX2dpdGh1Yigic2FleXNsYWIvbmljaGVuZXRyIikNCiNpbnN0YWxsLnBhY2thZ2VzKCdjaXJjbGl6ZScpDQpsaWJyYXJ5KG5pY2hlbmV0cikNCmxpYnJhcnkoU2V1cmF0KQ0KbGlicmFyeShjaXJjbGl6ZSkNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTB9DQojIEdldCBjb3VudHMgZGF0YQ0KY291bnRzIDwtIGFzLmRhdGEuZnJhbWUodGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1sicV9ub3JtIl1dKQ0KY291bnRzJFggPC0gcm93bmFtZXMoY291bnRzKQ0KI3Jvd25hbWVzKGNvdW50cykgPC0gTlVMTA0KI2NvdW50cyA8LSBhcy5tYXRyaXgoY291bnRzKQ0KDQojIEdldCBtZXRhIGRhdGENCm1ldGEgPC0gcERhdGEodGFyZ2V0X0RhdGEpDQptZXRhIDwtIG1ldGFbYygiQU5OMSIsICJBTk4yIiwgInNsaWRlX25hbWUiKV0NCm1ldGEkU2FtcGxlSUQgPC0gcHJvdG9jb2xEYXRhKHRhcmdldF9EYXRhKVtbIlNhbXBsZUlEIl1dDQptZXRhJHJvaSA8LSBwcm90b2NvbERhdGEodGFyZ2V0X0RhdGEpW1sicm9pIl1dDQoNCiMgY3JlYXRlIFNldXJhdCBmb3IgYmV0dGVyIG5pY2hlIGNvb3BlcmF0aW9uDQpTZXVyYXQuRGF0YSA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gY291bnRzLCBtZXRhLmRhdGEgPSBtZXRhKSANCg0KIyMgQ2hlY2sgbWV0YSBkYXRhDQojU2V1cmF0LkRhdGFAbWV0YS5kYXRhICU+JSBoZWFkKCkNCiNTZXVyYXQuRGF0YUBtZXRhLmRhdGEkUmVnaW9uICU+JSB0YWJsZSgpIA0KDQojIyBDaGFuZ2UgaWRlbnRzIHRvIGNvcnJlY3QgYW5ub3RhdGlvbg0KU2V1cmF0LkRhdGEgPC0gU2V0SWRlbnQoU2V1cmF0LkRhdGEsIHZhbHVlID0gIkFOTjEiKQ0KI1NldXJhdC5EYXRhQGFjdGl2ZS5pZGVudA0KDQojIyMjIyMjIFJlYWQgaW4gbGlnYW5kLXRhcmdldCBwcmlvciBtb2RlbCwgbGlnYW5kLXJlY2VwdG9yIG5ldHdvcmsgYW5kIHdlaWdodGVkIGludGVncmF0ZWQgbmV0d29ya3MNCmxpZ2FuZF90YXJnZXRfbWF0cml4ID0gcmVhZFJEUyh1cmwoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmQvMzI2MDc1OC9maWxlcy9saWdhbmRfdGFyZ2V0X21hdHJpeC5yZHMiKSkNCiNsaWdhbmRfdGFyZ2V0X21hdHJpeFsxOjUsMTo1XSAjIHRhcmdldCBnZW5lcyBpbiByb3dzLCBsaWdhbmRzIGluIGNvbHVtbnMNCg0KbHJfbmV0d29yayA9IHJlYWRSRFModXJsKCJodHRwczovL3plbm9kby5vcmcvcmVjb3JkLzMyNjA3NTgvZmlsZXMvbHJfbmV0d29yay5yZHMiKSkNCiNoZWFkKGxyX25ldHdvcmspDQoNCndlaWdodGVkX25ldHdvcmtzID0gcmVhZFJEUyh1cmwoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmQvMzI2MDc1OC9maWxlcy93ZWlnaHRlZF9uZXR3b3Jrcy5yZHMiKSkNCndlaWdodGVkX25ldHdvcmtzX2xyID0gd2VpZ2h0ZWRfbmV0d29ya3MkbHJfc2lnICU+JSBpbm5lcl9qb2luKGxyX25ldHdvcmsgJT4lIGRpc3RpbmN0KGZyb20sdG8pLCBieSA9IGMoImZyb20iLCJ0byIpKQ0KI2hlYWQod2VpZ2h0ZWRfbmV0d29ya3MkbHJfc2lnKSAjIGludGVyYWN0aW9ucyBhbmQgdGhlaXIgd2VpZ2h0cyBpbiB0aGUgbGlnYW5kLXJlY2VwdG9yICsgc2lnbmFsaW5nIG5ldHdvcmsNCg0KDQojIyMjIERlZmluZSBhIHNlbmRlciBhbmQgcmVjZWl2ZXIgcG9wdWxhdGlvbiBhbmQgZGV0ZXJtaW5lIHdoaWNoIGdlbmVzIGFyZSBleHByZXNzZWQgaW4gYm90aCBwb3B1bGF0aW9ucw0KDQojIyByZWNlaXZlcg0KcmVjZWl2ZXIgPSBjKCJDRDEwKyIpDQpleHByZXNzZWRfZ2VuZXNfcmVjZWl2ZXIgPSBnZXRfZXhwcmVzc2VkX2dlbmVzKHJlY2VpdmVyLCBTZXVyYXQuRGF0YSwgcGN0ID0gMC4xMCkNCmJhY2tncm91bmRfZXhwcmVzc2VkX2dlbmVzID0gZXhwcmVzc2VkX2dlbmVzX3JlY2VpdmVyICU+JSAuWy4gJWluJSByb3duYW1lcyhsaWdhbmRfdGFyZ2V0X21hdHJpeCldDQoNCiMjIHNlbmRlcg0Kc2VuZGVyX2NlbGx0eXBlcyA9IGMoIlBhbkNLKyIpDQpsaXN0X2V4cHJlc3NlZF9nZW5lc19zZW5kZXIgPSBzZW5kZXJfY2VsbHR5cGVzICU+JSB1bmlxdWUoKSAlPiUgbGFwcGx5KGdldF9leHByZXNzZWRfZ2VuZXMsIFNldXJhdC5EYXRhLCAwLjEwKSAjIGxhcHBseSB0byBnZXQgdGhlIGV4cHJlc3NlZCBnZW5lcyBvZiBldmVyeSBzZW5kZXIgY2VsbCB0eXBlIHNlcGFyYXRlbHkgaGVyZQ0KZXhwcmVzc2VkX2dlbmVzX3NlbmRlciA9IGxpc3RfZXhwcmVzc2VkX2dlbmVzX3NlbmRlciAlPiUgdW5saXN0KCkgJT4lIHVuaXF1ZSgpDQoNCiMjIyMgSW1wb3J0IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBpbiByZWNpZXZlciBwb3B1bGF0aW9ucw0KZ2VuZXNldC5vaSA8LSB0b3BfZw0KDQojIyMjIyAgRGVmaW5lIGEgc2V0IG9mIGxpZ2FuZHMgaW4gc2VuZGVyIHBvcHVsYXRpb25zDQpsaWdhbmRzID0gbHJfbmV0d29yayAlPiUgcHVsbChmcm9tKSAlPiUgdW5pcXVlKCkNCnJlY2VwdG9ycyA9IGxyX25ldHdvcmsgJT4lIHB1bGwodG8pICU+JSB1bmlxdWUoKQ0KDQpleHByZXNzZWRfbGlnYW5kcyA9IGludGVyc2VjdChsaWdhbmRzLGV4cHJlc3NlZF9nZW5lc19zZW5kZXIpDQpleHByZXNzZWRfcmVjZXB0b3JzID0gaW50ZXJzZWN0KHJlY2VwdG9ycyxleHByZXNzZWRfZ2VuZXNfcmVjZWl2ZXIpDQoNCnBvdGVudGlhbF9saWdhbmRzID0gbHJfbmV0d29yayAlPiUgZmlsdGVyKGZyb20gJWluJSBleHByZXNzZWRfbGlnYW5kcyAmIHRvICVpbiUgZXhwcmVzc2VkX3JlY2VwdG9ycykgJT4lIHB1bGwoZnJvbSkgJT4lIHVuaXF1ZSgpDQoNCg0KIyMjIyMjIyMjIyMjIyMNCiMjIyMjIyMjIyMjIFBlcmZvcm0gTmljaGVuZXQgYW5hbHlzaXMNCg0KDQpsaWdhbmRfYWN0aXZpdGllcyA9IHByZWRpY3RfbGlnYW5kX2FjdGl2aXRpZXMoZ2VuZXNldCA9IGdlbmVzZXQub2ksIGJhY2tncm91bmRfZXhwcmVzc2VkX2dlbmVzID0gYmFja2dyb3VuZF9leHByZXNzZWRfZ2VuZXMsIGxpZ2FuZF90YXJnZXRfbWF0cml4ID0gbGlnYW5kX3RhcmdldF9tYXRyaXgsIHBvdGVudGlhbF9saWdhbmRzID0gcG90ZW50aWFsX2xpZ2FuZHMpDQoNCmxpZ2FuZF9hY3Rpdml0aWVzID0gbGlnYW5kX2FjdGl2aXRpZXMgJT4lIGFycmFuZ2UoLXBlYXJzb24pIA0KI2xpZ2FuZF9hY3Rpdml0aWVzID0gbGlnYW5kX2FjdGl2aXRpZXMgJT4lIGFycmFuZ2UoLXBlYXJzb24pICU+JSBtdXRhdGUocmFuayA9IHJhbmsoZGVzYyhwZWFyc29uKSkpDQoNCg0KIyMjIyMjIyMjIyMjIyMNCiMjIyMjIyMgSGlzdG9ncmFtIG9mIGxpZ2FuZCBhY3Rpdml0aWVzDQoNCnBfaGlzdF9saWdfYWN0aXZpdHkgPSBnZ3Bsb3QobGlnYW5kX2FjdGl2aXRpZXMsIGFlcyh4PXBlYXJzb24pKSArIA0KICBnZW9tX2hpc3RvZ3JhbShjb2xvcj0iYmxhY2siLCBmaWxsPSJkYXJrb3JhbmdlIikgICsgDQogICMgZ2VvbV9kZW5zaXR5KGFscGhhPS4xLCBmaWxsPSJvcmFuZ2UiKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9bWluKGxpZ2FuZF9hY3Rpdml0aWVzICU+JSB0b3BfbigyMCwgcGVhcnNvbikgJT4lIHB1bGwocGVhcnNvbikpKSwgY29sb3I9InJlZCIsIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTEpICsgDQogIGxhYnMoeD0ibGlnYW5kIGFjdGl2aXR5IChQQ0MpIiwgeSA9ICIjIGxpZ2FuZHMiKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KcF9oaXN0X2xpZ19hY3Rpdml0eQ0KDQoNCiMjIyBEb3QgYmxvdCBvZiB0b3AgbGlnYW5kcyBzcGxpdCBieSByZWdpb24NCg0KYmVzdF91cHN0cmVhbV9saWdhbmRzID0gYyhsaWdhbmRfYWN0aXZpdGllcyAlPiUgdG9wX24oMjAsIHBlYXJzb24pICU+JSBhcnJhbmdlKC1wZWFyc29uKSAlPiUgcHVsbCh0ZXN0X2xpZ2FuZCkgJT4lIHVuaXF1ZSgpKQ0KDQojIHRlc3QgPC0gU2V1cmF0LkRhdGENCiMgeWVldCA8LSBjKCJXTlQzQSIpDQojIGRhdGEoInBibWNfc21hbGwiKQ0KIyB0ZXN0W1siUk5BIl1dQHZhci5mZWF0dXJlcyA8LSBiZXN0X3Vwc3RyZWFtX2xpZ2FuZHMNCiMgI2RhdGEuZnJhbWUocm93Lm5hbWVzID0gcm93bmFtZXModGVzdFtbIlJOQSJdXSkpDQojIA0KIyBEb3RQbG90KHRlc3QsIGZlYXR1cmVzID0gYmVzdF91cHN0cmVhbV9saWdhbmRzLCBjb2xzID0gIlJkWWxCdSIpICsgUm90YXRlZEF4aXMoKQ0KDQojIyBlbHNlDQoNCmFjdGl2ZV9saWdhbmRfdGFyZ2V0X2xpbmtzX2RmID0gYmVzdF91cHN0cmVhbV9saWdhbmRzICU+JSBsYXBwbHkoZ2V0X3dlaWdodGVkX2xpZ2FuZF90YXJnZXRfbGlua3MsZ2VuZXNldCA9IGdlbmVzZXQub2ksIGxpZ2FuZF90YXJnZXRfbWF0cml4ID0gbGlnYW5kX3RhcmdldF9tYXRyaXgsIG4gPSAyNTApICU+JSBiaW5kX3Jvd3MoKQ0KYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3MgPSBwcmVwYXJlX2xpZ2FuZF90YXJnZXRfdmlzdWFsaXphdGlvbihsaWdhbmRfdGFyZ2V0X2RmID0gYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYsIGxpZ2FuZF90YXJnZXRfbWF0cml4ID0gbGlnYW5kX3RhcmdldF9tYXRyaXgsIGN1dG9mZiA9IDAuMjUpDQoNCm9yZGVyX2xpZ2FuZHMgPSBpbnRlcnNlY3QoYmVzdF91cHN0cmVhbV9saWdhbmRzLCBjb2xuYW1lcyhhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rcykpICU+JSByZXYoKQ0Kb3JkZXJfdGFyZ2V0cyA9IGFjdGl2ZV9saWdhbmRfdGFyZ2V0X2xpbmtzX2RmJHRhcmdldCAlPiUgdW5pcXVlKCkNCnZpc19saWdhbmRfdGFyZ2V0ID0gYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3Nbb3JkZXJfdGFyZ2V0cyxvcmRlcl9saWdhbmRzXSAlPiUgdCgpDQoNCnBfbGlnYW5kX3RhcmdldF9uZXR3b3JrID0gdmlzX2xpZ2FuZF90YXJnZXQgJT4lIG1ha2VfaGVhdG1hcF9nZ3Bsb3QoIlByaW9yaXRpemVkIFBhbkNLKyBsaWdhbmRzIiwiQ0QxMCsgcG90ZW50aWFsIHJlY2VwdG9yIGNlbGxzIiwgY29sb3IgPSAicHVycGxlIixsZWdlbmRfcG9zaXRpb24gPSAidG9wIiwgeF9heGlzX3Bvc2l0aW9uID0gInRvcCIsbGVnZW5kX3RpdGxlID0gIlJlZ3VsYXRvcnkgcG90ZW50aWFsIikgKyBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAid2hpdGVzbW9rZSIsICBoaWdoID0gInB1cnBsZSIsIGJyZWFrcyA9IGMoMCwwLjAwNSwwLjAxKSkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYWNlID0gIml0YWxpYyIpKQ0KDQpwX2xpZ2FuZF90YXJnZXRfbmV0d29yaw0KDQojIyBjaXJjb3MNCg0KIyBRQyBvbiBxdWFudGlsZQ0KY3V0b2ZmX2luY2x1ZGVfYWxsX2xpZ2FuZHMgPSBhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZiR3ZWlnaHQgJT4lIHF1YW50aWxlKDAuNjYpDQphY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZl9jaXJjb3MgPSBhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZiAlPiUgZmlsdGVyKHdlaWdodCA+IGN1dG9mZl9pbmNsdWRlX2FsbF9saWdhbmRzKQ0KDQpsaWdhbmRzX3RvX3JlbW92ZSA9IHNldGRpZmYoYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYkbGlnYW5kICU+JSB1bmlxdWUoKSwgYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGZfY2lyY29zJGxpZ2FuZCAlPiUgdW5pcXVlKCkpDQp0YXJnZXRzX3RvX3JlbW92ZSA9IHNldGRpZmYoYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYkdGFyZ2V0ICU+JSB1bmlxdWUoKSwgYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGZfY2lyY29zJHRhcmdldCAlPiUgdW5pcXVlKCkpDQogIA0KY2lyY29zX2xpbmtzID0gYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYgJT4lIGZpbHRlcighdGFyZ2V0ICVpbiUgdGFyZ2V0c190b19yZW1vdmUgJiFsaWdhbmQgJWluJSBsaWdhbmRzX3RvX3JlbW92ZSkNCg0KIyBDb2xvcg0KZ3JpZF9jb2xfbGlnYW5kID1jKCJQYW5DSysiID0gInJlZCINCiAgICAgICAgICAgICAgICAgICMsIkdlbmVyYWwiID0gImdyZWVuIg0KICAgICAgICAgICAgICAgICAgICkNCmdyaWRfY29sX3RhcmdldCA9YygiQ0QxMCsiID0gImdyZWVuIikNCg0KZ3JpZF9jb2xfdGJsX2xpZ2FuZCA9IHRpYmJsZShsaWdhbmRfdHlwZSA9IGdyaWRfY29sX2xpZ2FuZCAlPiUgbmFtZXMoKSwgY29sb3JfbGlnYW5kX3R5cGUgPSBncmlkX2NvbF9saWdhbmQpDQpncmlkX2NvbF90YmxfdGFyZ2V0ID0gdGliYmxlKHRhcmdldF90eXBlID0gZ3JpZF9jb2xfdGFyZ2V0ICU+JSBuYW1lcygpLCBjb2xvcl90YXJnZXRfdHlwZSA9IGdyaWRfY29sX3RhcmdldCkNCg0KY2lyY29zX2xpbmtzID0gY2lyY29zX2xpbmtzICU+JSBtdXRhdGUobGlnYW5kID0gcGFzdGUobGlnYW5kLCIgIikpICMgZXh0cmEgc3BhY2U6IG1ha2UgYSBkaWZmZXJlbmNlIGJldHdlZW4gYSBnZW5lIGFzIGxpZ2FuZCBhbmQgYSBnZW5lIGFzIHRhcmdldCENCmNpcmNvc19saW5rcyA9IGNpcmNvc19saW5rcyAlPiUgaW5uZXJfam9pbihncmlkX2NvbF90YmxfbGlnYW5kLCBieSA9IGNoYXJhY3RlcigpKSAlPiUgaW5uZXJfam9pbihncmlkX2NvbF90YmxfdGFyZ2V0LCBieSA9IGNoYXJhY3RlcigpKQ0KbGlua3NfY2lyY2xlID0gY2lyY29zX2xpbmtzICU+JSBzZWxlY3QobGlnYW5kLHRhcmdldCwgd2VpZ2h0KQ0KDQpsaWdhbmRfY29sb3IgPSBjaXJjb3NfbGlua3MgJT4lIGRpc3RpbmN0KGxpZ2FuZCxjb2xvcl9saWdhbmRfdHlwZSkNCmdyaWRfbGlnYW5kX2NvbG9yID0gbGlnYW5kX2NvbG9yJGNvbG9yX2xpZ2FuZF90eXBlICU+JSBzZXRfbmFtZXMobGlnYW5kX2NvbG9yJGxpZ2FuZCkNCnRhcmdldF9jb2xvciA9IGNpcmNvc19saW5rcyAlPiUgZGlzdGluY3QodGFyZ2V0LGNvbG9yX3RhcmdldF90eXBlKQ0KZ3JpZF90YXJnZXRfY29sb3IgPSB0YXJnZXRfY29sb3IkY29sb3JfdGFyZ2V0X3R5cGUgJT4lIHNldF9uYW1lcyh0YXJnZXRfY29sb3IkdGFyZ2V0KQ0KDQpncmlkX2NvbCA9IGMoZ3JpZF9saWdhbmRfY29sb3IsZ3JpZF90YXJnZXRfY29sb3IpDQoNCiMgZ2l2ZSB0aGUgb3B0aW9uIHRoYXQgbGlua3MgaW4gdGhlIGNpcmNvcyBwbG90IHdpbGwgYmUgdHJhbnNwYXJhbnQgfiBsaWdhbmQtdGFyZ2V0IHBvdGVudGlhbCBzY29yZQ0KdHJhbnNwYXJlbmN5ID0gY2lyY29zX2xpbmtzICU+JSBtdXRhdGUod2VpZ2h0ID0od2VpZ2h0LW1pbih3ZWlnaHQpKS8obWF4KHdlaWdodCktbWluKHdlaWdodCkpKSAlPiUgbXV0YXRlKHRyYW5zcGFyZW5jeSA9IDEtd2VpZ2h0KSAlPiUgLiR0cmFuc3BhcmVuY3kgDQoNCiMgU3BlY2lmaWMgb3JkZXIgd2l0aCBnYXBzIGlmIG5lZWRlZA0KdGFyZ2V0X29yZGVyID0gY2lyY29zX2xpbmtzJHRhcmdldCAlPiUgdW5pcXVlKCkNCiNsaWdhbmRfb3JkZXIgPSBjKENBRl9zcGVjaWZpY19saWdhbmRzLGdlbmVyYWxfbGlnYW5kcyxlbmRvdGhlbGlhbF9zcGVjaWZpY19saWdhbmRzKSAlPiUgYyhwYXN0ZSguLCIgIikpICU+JSBpbnRlcnNlY3QoY2lyY29zX2xpbmtzJGxpZ2FuZCkNCmxpZ2FuZF9vcmRlciA9IGMoKSAlPiUgYyhwYXN0ZSguLCIgIikpICU+JSBpbnRlcnNlY3QoY2lyY29zX2xpbmtzJGxpZ2FuZCkNCm9yZGVyID0gYyhsaWdhbmRfb3JkZXIsdGFyZ2V0X29yZGVyKQ0KDQp3aWR0aF9zYW1lX2NlbGxfc2FtZV9saWdhbmRfdHlwZSA9IDAuNQ0Kd2lkdGhfZGlmZmVyZW50X2NlbGwgPSA2DQp3aWR0aF9saWdhbmRfdGFyZ2V0ID0gMTUNCndpZHRoX3NhbWVfY2VsbF9zYW1lX3RhcmdldF90eXBlID0gMC41DQoNCmdhcHMgPSBjKA0KICAjIHdpZHRoX2xpZ2FuZF90YXJnZXQsDQogIHJlcCh3aWR0aF9zYW1lX2NlbGxfc2FtZV9saWdhbmRfdHlwZSwgdGltZXMgPSAoY2lyY29zX2xpbmtzICU+JSBmaWx0ZXIobGlnYW5kX3R5cGUgPT0gIlBhbkNLKyIpICU+JSBkaXN0aW5jdChsaWdhbmQpICU+JSBucm93KCkgLTEpKSwNCiAgd2lkdGhfZGlmZmVyZW50X2NlbGwsDQogICMgcmVwKHdpZHRoX3NhbWVfY2VsbF9zYW1lX2xpZ2FuZF90eXBlLCB0aW1lcyA9IChjaXJjb3NfbGlua3MgJT4lIGZpbHRlcihsaWdhbmRfdHlwZSA9PSAiR2VuZXJhbCIpICU+JSBkaXN0aW5jdChsaWdhbmQpICU+JSBucm93KCkgLTEpKSwNCiAgIyB3aWR0aF9kaWZmZXJlbnRfY2VsbCwNCiAgI3JlcCh3aWR0aF9zYW1lX2NlbGxfc2FtZV9saWdhbmRfdHlwZSwgdGltZXMgPSAoY2lyY29zX2xpbmtzICU+JSBmaWx0ZXIobGlnYW5kX3R5cGUgPT0gIkVuZG90aGVsaWFsLXNwZWNpZmljIikgJT4lIGRpc3RpbmN0KGxpZ2FuZCkgJT4lIG5yb3coKSAtMSkpLCANCiAgI3dpZHRoX2xpZ2FuZF90YXJnZXQsDQogIHJlcCh3aWR0aF9zYW1lX2NlbGxfc2FtZV90YXJnZXRfdHlwZSwgdGltZXMgPSAoY2lyY29zX2xpbmtzICU+JSBmaWx0ZXIodGFyZ2V0X3R5cGUgPT0gIkNEMTArIikgJT4lIGRpc3RpbmN0KHRhcmdldCkgJT4lIG5yb3coKSAtMSkpLA0KICB3aWR0aF9saWdhbmRfdGFyZ2V0DQogICkNCg0KI2NpcmNvcy5wYXIoZ2FwLmRlZ3JlZSA9IGdhcHMpDQoNCmNpcmNvcy5wYXIoUkVTRVQgPSBUUlVFKQ0KDQpjaG9yZERpYWdyYW0obGlua3NfY2lyY2xlLCANCiAgICAgICAgICAgICBkaXJlY3Rpb25hbCA9IDEsDQogICAgICAgICAgICAgI29yZGVyPW9yZGVyLA0KICAgICAgICAgICAgIGxpbmsuc29ydCA9IFRSVUUsIA0KICAgICAgICAgICAgIGxpbmsuZGVjcmVhc2luZyA9IEZBTFNFLCANCiAgICAgICAgICAgICBncmlkLmNvbCA9IGdyaWRfY29sLA0KICAgICAgICAgICAgIHRyYW5zcGFyZW5jeSA9IHRyYW5zcGFyZW5jeSwgDQogICAgICAgICAgICAgZGlmZkhlaWdodCA9IDAuMDA1LCANCiAgICAgICAgICAgICBkaXJlY3Rpb24udHlwZSA9IGMoImRpZmZIZWlnaHQiLCAiYXJyb3dzIiksDQogICAgICAgICAgICAgbGluay5hcnIudHlwZSA9ICJiaWcuYXJyb3ciLCANCiAgICAgICAgICAgICBsaW5rLnZpc2libGUgPSBsaW5rc19jaXJjbGUkd2VpZ2h0ID49IGN1dG9mZl9pbmNsdWRlX2FsbF9saWdhbmRzLA0KICAgICAgICAgICAgIGFubm90YXRpb25UcmFjayA9ICJncmlkIiwNCiAgICAgICAgICAgICBwcmVBbGxvY2F0ZVRyYWNrcyA9IGxpc3QodHJhY2suaGVpZ2h0ID0gMC4wNzUpKQ0KICAgICAgICAgICAgICNwcmVBbGxvY2F0ZVRyYWNrcyA9IGxpc3QodHJhY2suaGVpZ2h0ID0gbWF4KHN0cndpZHRoKHVubGlzdChkaW1uYW1lcyhsaW5rc19jaXJjbGUpKSkpKSkgIA0KICAgICAgICAgICAgIA0KIyB3ZSBnbyBiYWNrIHRvIHRoZSBmaXJzdCB0cmFjayBhbmQgY3VzdG9taXplIHNlY3RvciBsYWJlbHMNCmNpcmNvcy50cmFjayh0cmFjay5pbmRleCA9IDEsIHBhbmVsLmZ1biA9IGZ1bmN0aW9uKHgsIHkpIHsNCiAgICBjaXJjb3MudGV4dChDRUxMX01FVEEkeGNlbnRlciwgQ0VMTF9NRVRBJHlsaW1bMV0sIENFTExfTUVUQSRzZWN0b3IuaW5kZXgsDQogICAgICAgIGZhY2luZyA9ICJjbG9ja3dpc2UiLCBuaWNlRmFjaW5nID0gVFJVRSwgYWRqID0gYygwLCAwLjU1KSwgY2V4ID0gMSkNCn0sIGJnLmJvcmRlciA9IE5BKSANCg0KbGVnZW5kKCJib3R0b21sZWZ0IiwgDQogICAgICAgdGl0bGUgPSAiQ2VsbCBzcGVjaWZpYyIsDQogICAgICAgY2V4PSAzLCANCiAgICAgICBsZWdlbmQgPSB1bmlxdWUoYyhjaXJjb3NfbGlua3MkbGlnYW5kX3R5cGUsIHVuaXF1ZShjaXJjb3NfbGlua3MkdGFyZ2V0X3R5cGUpKSksDQogICAgICAgZmlsbCA9IHVuaXF1ZShjKGdyaWRfY29sKSkpDQp0aXRsZSgiTGlnYW5kLXJlY2VwdG9yIGNvbm5lY3Rpb25zIikNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTI1LGZpZy5oZWlnaHQ9MTB9DQojIGdldCB0aGUgbGlnYW5kLXJlY2VwdG9yIG5ldHdvcmsgb2YgdGhlIHRvcC1yYW5rZWQgbGlnYW5kcw0KbHJfbmV0d29ya190b3AgPSBscl9uZXR3b3JrICU+JSBmaWx0ZXIoZnJvbSAlaW4lIGJlc3RfdXBzdHJlYW1fbGlnYW5kcyAmIHRvICVpbiUgZXhwcmVzc2VkX3JlY2VwdG9ycykgJT4lIGRpc3RpbmN0KGZyb20sdG8pDQpiZXN0X3Vwc3RyZWFtX3JlY2VwdG9ycyA9IGxyX25ldHdvcmtfdG9wICU+JSBwdWxsKHRvKSAlPiUgdW5pcXVlKCkNCg0KIyAjIGdldCB0aGUgd2VpZ2h0cyBvZiB0aGUgbGlnYW5kLXJlY2VwdG9yIGludGVyYWN0aW9ucyBhcyB1c2VkIGluIHRoZSBOaWNoZU5ldCBtb2RlbA0KIyB3ZWlnaHRlZF9uZXR3b3JrcyA9IHJlYWRSRFModXJsKCJodHRwczovL3plbm9kby5vcmcvcmVjb3JkLzMyNjA3NTgvZmlsZXMvd2VpZ2h0ZWRfbmV0d29ya3MucmRzIikpDQpscl9uZXR3b3JrX3RvcF9kZiA9IHdlaWdodGVkX25ldHdvcmtzJGxyX3NpZyAlPiUgZmlsdGVyKGZyb20gJWluJSBiZXN0X3Vwc3RyZWFtX2xpZ2FuZHMgJiB0byAlaW4lIGJlc3RfdXBzdHJlYW1fcmVjZXB0b3JzKQ0KDQojIGNvbnZlcnQgdG8gYSBtYXRyaXgNCmxyX25ldHdvcmtfdG9wX2RmID0gbHJfbmV0d29ya190b3BfZGYgJT4lIHNwcmVhZCgiZnJvbSIsIndlaWdodCIsZmlsbCA9IDApDQpscl9uZXR3b3JrX3RvcF9tYXRyaXggPSBscl9uZXR3b3JrX3RvcF9kZiAlPiUgc2VsZWN0KC10bykgJT4lIGFzLm1hdHJpeCgpICU+JSBtYWdyaXR0cjo6c2V0X3Jvd25hbWVzKGxyX25ldHdvcmtfdG9wX2RmJHRvKQ0KDQojIHBlcmZvcm0gaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgdG8gb3JkZXIgdGhlIGxpZ2FuZHMgYW5kIHJlY2VwdG9ycw0KZGlzdF9yZWNlcHRvcnMgPSBkaXN0KGxyX25ldHdvcmtfdG9wX21hdHJpeCwgbWV0aG9kID0gImJpbmFyeSIpDQpoY2x1c3RfcmVjZXB0b3JzID0gaGNsdXN0KGRpc3RfcmVjZXB0b3JzLCBtZXRob2QgPSAid2FyZC5EMiIpDQpvcmRlcl9yZWNlcHRvcnMgPSBoY2x1c3RfcmVjZXB0b3JzJGxhYmVsc1toY2x1c3RfcmVjZXB0b3JzJG9yZGVyXQ0KDQpkaXN0X2xpZ2FuZHMgPSBkaXN0KGxyX25ldHdvcmtfdG9wX21hdHJpeCAlPiUgdCgpLCBtZXRob2QgPSAiYmluYXJ5IikNCmhjbHVzdF9saWdhbmRzID0gaGNsdXN0KGRpc3RfbGlnYW5kcywgbWV0aG9kID0gIndhcmQuRDIiKQ0Kb3JkZXJfbGlnYW5kc19yZWNlcHRvciA9IGhjbHVzdF9saWdhbmRzJGxhYmVsc1toY2x1c3RfbGlnYW5kcyRvcmRlcl0NCg0KDQp2aXNfbGlnYW5kX3JlY2VwdG9yX25ldHdvcmsgPSBscl9uZXR3b3JrX3RvcF9tYXRyaXhbb3JkZXJfcmVjZXB0b3JzLCBvcmRlcl9saWdhbmRzX3JlY2VwdG9yXQ0KcF9saWdhbmRfcmVjZXB0b3JfbmV0d29yayA9IHZpc19saWdhbmRfcmVjZXB0b3JfbmV0d29yayAlPiUgdCgpICU+JSBtYWtlX2hlYXRtYXBfZ2dwbG90KCJQcmlvcml0aXplZCBQYW5DSytsaWdhbmRzIiwiUmVjZXB0b3JzIGV4cHJlc3NlZCBieSBDRDEwKyIsIGNvbG9yID0gIm1lZGl1bXZpb2xldHJlZCIsIHhfYXhpc19wb3NpdGlvbiA9ICJ0b3AiLGxlZ2VuZF90aXRsZSA9ICJQcmlvciBpbnRlcmFjdGlvbiBwb3RlbnRpYWwiKQ0KcF9saWdhbmRfcmVjZXB0b3JfbmV0d29yaw0KYGBgDQoNCiMgQ05WDQoNCmBgYHtyfQ0KI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJpbmZlcmNudiIpDQpsaWJyYXJ5KGluZmVyY252KQ0KI2RldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiYWVyaWNrc28vU3BhdGlhbEluZmVyQ05WIikNCiNsaWJyYXJ5KFNwYXRpYWxJbmZlckNOVikNCmBgYA0KDQpgYGB7ciBjcmVhdGUgY252IGZpbGVzX30NCmZpbGUgPC0gcmVhZExpbmVzKFBLQ0ZpbGVzKQ0KZmlsZSA8LSBnc3ViKCcgJywgJycsDQogICAgICAgIGdzdWIoJyInLCAnJywgZmlsZSkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEZvcm1hdCBhbmQgcmVtb3ZlIHNwYWNlDQoNCmRpc3BsYXkgPC0gZmlsZVtncmVwbCgiRGlzcGxheU5hbWUiLCBmaWxlLCBmaXhlZCA9IFRSVUUpXSAgICAgICAgICMgR3JhYiBkaXNwbGF5IG5hbWVzDQpkaXNwbGF5IDwtIGdzdWIoJ0Rpc3BsYXlOYW1lJywgIiIsDQogICAgICAgICAgIGdzdWIoJyInLCAiIiwNCiAgICAgICAgICAgZ3N1YignOicsICIiLA0KICAgICAgICAgICBnc3ViKCcsJywgIiIsDQogICAgICAgICAgIGdzdWIoJyAnLCAiIiwNCiAgICAgICAgICAgZ3N1YignXzAxJywgIiIsIGRpc3BsYXkpKSkpKSkgICAgICAgICAgICAgICAgICAgICAgICAgICMgR3JhYiBvbmx5IHRoZSBuYW1lcw0KZGlzcGxheSA8LSB1bmlxdWUoZGlzcGxheSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSZW1vdmUgZHVwbGljYXRlcw0KDQpjaHIgPC0gZmlsZVtncmVwKCJHZW5vbWVDb29yZGluYXRlcyIsIGZpbGUpKzFdICAgICAgICAgICAgICAgICAgICAjIEdldCB0aGUgY2hyIHBvc2l0aW9ucyB1bmRlciB0aGUgR2Vub21lQ29vcmRpbmF0ZXMgbGluZQ0KY2hyIDwtZ3N1YignLCcsICIiLCBjaHIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSZW1vdmUgdW53YW50ZWQgc3ltYm9scw0KDQpwb3NpdGlvbnMgPC0gZGF0YS5mcmFtZSgNCiAgbmFtZSA9IGRpc3BsYXksIA0KICBjaHIgPSBjaHIpDQpwb3NpdGlvbnMgPC0gcG9zaXRpb25zWyFncmVwbCgiVGFyZ2V0U2VxdWVuY2UiLCBwb3NpdGlvbnMkY2hyKSxdICAjIFJlbW92ZSBlbnRyaWVzIHdpdGhvdXQgY29vcmRpbmF0ZXMNCg0KcG9zaXRpb25zJGNociA8LSBnc3ViKCc6JywgJy0nLCBwb3NpdGlvbnMkY2hyKSAgICAgICAgICAgICAgICAgICAgIyBGb3JtYXQgZm9yIHNwbGl0dGluZw0KcG9zaXRpb25zJGNociA8LSBzdHJfc3BsaXRfZml4ZWQocG9zaXRpb25zJGNociwgIi0iLCAzKSAgICAgICAgICAgIyBTcGxpdCBpbnRvIGNociwgYmVnaW4sIGFuZCBlbmQgcG9zaXRpb24NCnBvc2l0aW9ucyA8LSBhcy5tYXRyaXgocG9zaXRpb25zKSANCnBvc2l0aW9ucyA8LSBhcy5kYXRhLmZyYW1lKHBvc2l0aW9ucykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU3dhcCB0eXBlcyB0byByZWNvZ25pemUgc3BsaXQgY29sdW1ucyBhcyBzaW5ndWxhciBjb2x1bW5zDQpjb2xuYW1lcyhwb3NpdGlvbnMpIDwtIE5VTEwNCnJvd25hbWVzKHBvc2l0aW9ucykgPC0gTlVMTCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW5mZXJDTlYgcmVxdWlyZXMgbm8gY29sdW1uIGFuZCByb3cgbmFtZXMNCg0Kd3JpdGUudGFibGUocG9zaXRpb25zLCAiZ2VuZV9vcmRlcl9Ic19XVEFfdjFfcGtjLnR4dCIsIA0KICAgICAgICAgICAgc2VwID0gIlx0IiwNCiAgICAgICAgICAgIHF1b3RlID0gRkFMU0UsIA0KICAgICAgICAgICAgY29sLm5hbWVzID0gRkFMU0UsIA0KICAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBXcml0ZSBhd2F5DQoNCmNvdW50cyA8LSB0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJleHBycyJdXQ0Kd3JpdGUudGFibGUoY291bnRzLCAiQ291bnRzLnRzdiIsIHNlcCA9ICJcdCIpICAgICAgICAgICAgICAgICAgICAjIE1ha2UgcmF3IGNvdW50IGZpbGUNCg0KYW5ub3RhdGlvbiA8LSB0YXJnZXRfRGF0YUBwaGVub0RhdGFAZGF0YQ0KYW5ub3RhdGlvbiRTYW1wbGVJRCA8LSByb3duYW1lcyhhbm5vdGF0aW9uKQ0KYW5ub3RhdGlvbiA8LSBhbm5vdGF0aW9uW2MoIlNhbXBsZUlEIiwgIkFOTjIiKV0gICAgICAgICAgICAgICAgICAjIFNlbGVjdCBTYW1wbGVJRCBhbmQgdGhlIG5lZWRlZCBhbm5vdGF0aW9uIChDTlYgcmVxdWlyZXMgU2FtcGxlSUQgYW5kIG9ubHkgMSBhbm5vdGF0aW9uKQ0Kcm93bmFtZXMoYW5ub3RhdGlvbikgPC0gTlVMTA0KY29sbmFtZXMoYW5ub3RhdGlvbikgPC0gTlVMTCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluZmVyQ05WIHJlcXVpcmVzIG5vIGNvbHVtbiBhbmQgcm93IG5hbWVzDQp3cml0ZS50YWJsZShhbm5vdGF0aW9uLCAiQW5ub3RhdGlvbnMudHN2IiwgDQogICAgICAgICAgICBzZXAgPSAiXHQiLA0KICAgICAgICAgICAgcXVvdGUgPSBGQUxTRSwgDQogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwgDQogICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWFrZSBhbm5vdGF0aW9uIGZpbGUNCmBgYA0KDQoNCmBgYHtyIHJ1biBDTlZfLCBpbmNsdWRlPUZBTFNFfQ0KIyBQaWNrIG5vcm1hbC9yZWZlcmVuY2UgZ3JvdXAocykNCnJlZmVyZW5jZSA8LSBjKCJDb3J0aWNhbCBnbG9tZXJ1bHVzIikNCg0KIyBOYW1lIG91dHB1dCBmb2xkZXINCm91dF9kaXIgPC0gIkNOViINCg0KIyBDcmVhdGUgdGhlIGluZmVyY252IG9iamVjdA0KaW5mZXJjbnZfb2JqID0gQ3JlYXRlSW5mZXJjbnZPYmplY3QocmF3X2NvdW50c19tYXRyaXg9ICJDb3VudHMudHN2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25zX2ZpbGU9ICJBbm5vdGF0aW9ucy50c3YiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVsaW09Ilx0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfb3JkZXJfZmlsZT0gImdlbmVfb3JkZXJfSHNfV1RBX3YxX3BrYy50eHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI2dlbmVfb3JkZXJfZmlsZT0gImdlbmNvZGVfdjE5X2dlbmVfcG9zLnR4dCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWZfZ3JvdXBfbmFtZXM9IHJlZmVyZW5jZSwgIyBpbnB1dCB0aGUgbm9ybWFsL3JlZmVyZW5jZSBncm91cCBuYW1lcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hyX2V4Y2x1ZGUgPSBjKCJjaHJNIikpICMgRGVmYXVsdCBleGNsdWRlcyBjaHJYLCBjaHJZIGFuZCBjaHJNLiBCeSBvbmx5IHBpY2tpbmcgY2hyTSB5b3UgaW5jbHVkZSB0aGUgWCBhbmQgWSBjaHJvbW9zb21lcy4gICAgIA0KDQojIHBlcmZvcm0gaW5mZXJjbnYgb3BlcmF0aW9ucyB0byByZXZlYWwgY252IHNpZ25hbC4gRm9yIGFsbCBvcHRpb25zOiBodHRwczovL3JkcnIuaW8vZ2l0aHViL2Jyb2FkaW5zdGl0dXRlL2luZmVyY252L21hbi9ydW4uaHRtbA0KaW5mZXJjbnZfb2JqID0gaW5mZXJjbnY6OnJ1bihpbmZlcmNudl9vYmosDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1dG9mZj0wLjEsICAjIHVzZSAxIGZvciBzbWFydC1zZXEsIDAuMSBmb3IgMTB4LWdlbm9taWNzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG91dF9kaXI9IG91dF9kaXIsICAjIGRpciBpcyBhdXRvLWNyZWF0ZWQgZm9yIHN0b3Jpbmcgb3V0cHV0cw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2J5X2dyb3Vwcz1GLCAgICMgY2x1c3Rlcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZW5vaXNlPVQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhNTT1GLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmFseXNpc19tb2RlID0gInN1YmNsdXN0ZXJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyxkZWJ1Zz1UUlVFDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCg0KICAgICAgICAgICMgIkdlbm9tZUNvb3JkaW5hdGVzIjogWw0KICAgICAgICAgICMgICAiY2hyMTM6MTEyMzc4NzMxLTExMjM5MzA2OSIsIA0KICAgICAgICAgICMgICAiY2hyMTM6MTEyMzg3Mzk2LTExMjM5MzA2OSIsIA0KICAgICAgICAgICMgICAiY2hyMTM6MTEyMzc2NDc5LTExMjM5MzA2OSINCmBgYA0KDQpgYGB7ciBzaG93IENOVl8sIGZpZy5oZWlnaHQ9MzAsIGZpZy53aWR0aD0yNX0NCmltZyA8LSByZWFkUE5HKHBhc3RlKG91dF9kaXIsICIvaW5mZXJjbnYucG5nIiwgc2VwID0gIiIpKQ0KZ3JpZDo6Z3JpZC5uZXdwYWdlKCkNCmdyaWQ6OmdyaWQucmFzdGVyKGltZykNCmBgYA0KDQo=